多轮审查和修复

This commit is contained in:
2026-05-12 15:34:08 +08:00
parent f55d2a57c3
commit ebbbb7332e
805 changed files with 838724 additions and 1905 deletions

View File

@@ -0,0 +1,107 @@
using BaseGames.Core.Events;
using BaseGames.Input;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace BaseGames.Cutscene
{
/// <summary>
/// Unity Timeline 过场动画封装(架构 14_NarrativeModule §11
/// 负责播放/停止 CutsceneSO切换 Action Map广播过场开始/结束事件。
/// </summary>
[RequireComponent(typeof(PlayableDirector))]
public class CutsceneManager : MonoBehaviour
{
[SerializeField] private InputReaderSO _inputReader;
[Header("事件频道")]
[SerializeField] private VoidEventChannelSO _onCutsceneStarted;
[SerializeField] private VoidEventChannelSO _onCutsceneEnded;
[SerializeField] private StringEventChannelSO _onPlayCutsceneById; // 由 PlayCutsceneAction 触发
[Header("过场素材库PlayById 查找用)")]
[SerializeField] private CutsceneSO[] _registeredCutscenes;
private PlayableDirector _director;
private readonly CompositeDisposable _subs = new();
/// <summary>是否正在播放过场。</summary>
public bool IsPlaying => _director != null && _director.state == PlayState.Playing;
// ── 生命周期 ──────────────────────────────────────────────────────
private void Awake()
{
_director = GetComponent<PlayableDirector>();
}
private void OnEnable()
{
_onPlayCutsceneById?.Subscribe(PlayById).AddTo(_subs);
}
private void OnDisable()
{
_subs.Clear();
}
// ── 公开 API ──────────────────────────────────────────────────────
/// <summary>通过 cutsceneId 查找并播放(供 PlayCutsceneAction 通过事件触发)。</summary>
public void PlayById(string cutsceneId)
{
if (_registeredCutscenes == null) return;
foreach (var cs in _registeredCutscenes)
if (cs != null && cs.cutsceneId == cutsceneId) { PlayCutscene(cs); return; }
Debug.LogWarning($"[CutsceneManager] 找不到 cutsceneId='{cutsceneId}'");
}
/// <summary>播放指定过场 SO。</summary>
public void PlayCutscene(CutsceneSO cutscene)
{
if (cutscene == null || IsPlaying) return;
_director.playableAsset = cutscene.Timeline;
// 应用 Track → GameObject 绑定
if (cutscene.Bindings != null && cutscene.Timeline != null)
{
var outputs = cutscene.Timeline.outputs;
int idx = 0;
foreach (var output in outputs)
{
if (idx >= cutscene.Bindings.Length) break;
var binding = cutscene.Bindings[idx];
if (!string.IsNullOrEmpty(binding.trackName) && output.streamName == binding.trackName
&& binding.target != null)
{
_director.SetGenericBinding(output.sourceObject, binding.target);
}
idx++;
}
}
_director.stopped += OnCutsceneStopped;
_director.Play();
_inputReader?.EnableUIInput();
_onCutsceneStarted?.Raise();
}
/// <summary>立即停止当前过场。</summary>
public void StopCutscene()
{
if (_director != null) _director.Stop();
}
// ── 内部 ──────────────────────────────────────────────────────────
private void OnCutsceneStopped(PlayableDirector director)
{
_director.stopped -= OnCutsceneStopped;
_inputReader?.EnableGameplayInput();
_onCutsceneEnded?.Raise();
}
}
}