using BaseGames.Core.Events;
using BaseGames.Input;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace BaseGames.Cutscene
{
///
/// Unity Timeline 过场动画封装(架构 14_NarrativeModule §11)。
/// 负责播放/停止 CutsceneSO;切换 Action Map;广播过场开始/结束事件。
///
[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();
/// 是否正在播放过场。
public bool IsPlaying => _director != null && _director.state == PlayState.Playing;
// ── 生命周期 ──────────────────────────────────────────────────────
private void Awake()
{
_director = GetComponent();
}
private void OnEnable()
{
_onPlayCutsceneById?.Subscribe(PlayById).AddTo(_subs);
}
private void OnDisable()
{
_subs.Clear();
}
// ── 公开 API ──────────────────────────────────────────────────────
/// 通过 cutsceneId 查找并播放(供 PlayCutsceneAction 通过事件触发)。
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}'");
}
/// 播放指定过场 SO。
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();
}
/// 立即停止当前过场。
public void StopCutscene()
{
if (_director != null) _director.Stop();
}
// ── 内部 ──────────────────────────────────────────────────────────
private void OnCutsceneStopped(PlayableDirector director)
{
_director.stopped -= OnCutsceneStopped;
_inputReader?.EnableGameplayInput();
_onCutsceneEnded?.Raise();
}
}
}