多轮审查和修复
This commit is contained in:
107
Assets/Scripts/Cutscene/CutsceneManager.cs
Normal file
107
Assets/Scripts/Cutscene/CutsceneManager.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user