using BaseGames.Core.Events; using BaseGames.World; using UnityEngine; namespace BaseGames.Cutscene { /// /// 过场动画触发器(架构 14_NarrativeModule §11.5)。 /// 支持四种触发模式:进入区域、玩家交互、场景加载、事件频道。 /// 实现 IInteractable 以支持 OnInteract 模式。 /// public class CutsceneTrigger : MonoBehaviour, IInteractable { public enum TriggerMode { OnEnter, // 进入 Trigger 碰撞区域 OnInteract, // 玩家主动交互(IInteractable) OnSceneLoad, // 场景加载完毕(Start) OnEvent, // 订阅事件频道触发 } [SerializeField] private CutsceneSO _cutscene; [SerializeField] private TriggerMode _mode = TriggerMode.OnEnter; [SerializeField] private CutsceneManager _cutsceneManager; [SerializeField] private VoidEventChannelSO _triggerEventChannel; // OnEvent 模式使用 [SerializeField] private WorldStateRegistry _worldState; // SO 注入,记录/查询播放状态 private readonly CompositeDisposable _subs = new(); // ── IInteractable(OnInteract 模式)────────────────────────────── public bool CanInteract => _mode == TriggerMode.OnInteract; public string InteractPrompt => "查看"; public void Interact(Transform player) => TriggerCutscene(); public void OnPlayerEnterRange(Transform player) { } public void OnPlayerExitRange() { } // ── 生命周期 ────────────────────────────────────────────────────── private void OnEnable() { if (_mode == TriggerMode.OnEvent) _triggerEventChannel?.Subscribe(TriggerCutscene).AddTo(_subs); } private void OnDisable() { _subs.Clear(); } private void Start() { if (_mode == TriggerMode.OnSceneLoad) TriggerCutscene(); } private void OnTriggerEnter2D(Collider2D other) { if (_mode != TriggerMode.OnEnter) return; if (!other.CompareTag("Player")) return; TriggerCutscene(); } // ── 触发逻辑 ────────────────────────────────────────────────────── private void TriggerCutscene() { if (_cutscene == null || _cutsceneManager == null) return; // 已播放且仅播一次 → 跳过 if (_cutscene.playOnlyOnce && _worldState != null && _worldState.HasFlag($"cutscene_played_{_cutscene.cutsceneId}")) return; // 捕获局部变量,避免回调内通过 this 访问被销毁的对象 var cutsceneId = _cutscene.cutsceneId; var worldState = _worldState; _cutsceneManager.PlayCutscene(_cutscene, onCompleted: () => { // 过场完全结束后才写入 flag,确保异常中断时可重触 worldState?.SetFlag($"cutscene_played_{cutsceneId}"); }); // 区域触发后禁用自身,防止重入 if (_mode == TriggerMode.OnEnter) enabled = false; } } }