Files
zeling_v2/Assets/Scripts/Cutscene/SignalEmitterClip.cs
2026-05-12 15:34:08 +08:00

64 lines
2.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using BaseGames.Core.Events;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace BaseGames.Cutscene
{
// =====================================================================
// SignalEmitterClip —— Timeline 零耦合事件桥接(架构 14 §11.6
// =====================================================================
/// <summary>
/// 在 Timeline 轨道上放置此 ClipClip 播放时向目标 Void 事件频道 Raise 一次事件。
/// 用途Timeline 动画与游戏逻辑保持零耦合(不直接引用场景对象)。
/// 使用场景示例:过场第 3 秒 → 触发 EVT_BossPhase2 → BossOrchestrator 切换阶段。
/// </summary>
[CreateAssetMenu(menuName = "BaseGames/Cutscene/SignalEmitterClip")]
public class SignalEmitterClip : PlayableAsset, ITimelineClipAsset
{
[Tooltip("Clip 播放时发射的目标 Void 事件频道 SO")]
[SerializeField] private VoidEventChannelSO _targetChannel;
/// <summary>Timeline 系统查询 Clip 能力(无额外能力)。</summary>
public ClipCaps clipCaps => ClipCaps.None;
public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
{
var playable = ScriptPlayable<SignalEmitterBehaviour>.Create(graph);
playable.GetBehaviour().Clip = this;
return playable;
}
/// <summary>供 SignalEmitterBehaviour 内部调用,发射事件。</summary>
internal void Fire() => _targetChannel?.Raise();
}
// ─────────────────────────────────────────────────────────────────────
/// <summary>
/// SignalEmitterClip 对应的 PlayableBehaviour处理 ProcessFrame 时机。
/// </summary>
public class SignalEmitterBehaviour : PlayableBehaviour
{
/// <summary>由 CreatePlayable 注入,内部访问 Fire() 发射事件。</summary>
public SignalEmitterClip Clip;
private bool _fired;
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
_fired = false; // 支持 Timeline 循环/重播时重置
}
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
if (!_fired && Clip != null)
{
Clip.Fire();
_fired = true;
}
}
}
}