Unity Timeline & Signal 이벤트 시스템
Unity Timeline은 시간 기반 시퀀서로 애니메이션, 오디오, 카메라 전환을 한 타임라인에서 조율합니다. Signal Track을 활용하면 타임라인의 특정 시점에 게임 로직 이벤트를 트리거할 수 있습니다.
1. PlayableDirector 기본 제어
섹션 제목: “1. PlayableDirector 기본 제어”using UnityEngine;using UnityEngine.Playables;
public class CutsceneManager : MonoBehaviour{ [SerializeField] PlayableDirector director;
public void PlayCutscene() { director.Play(); }
public void StopCutscene() { director.Stop(); }
public void SeekTo(double timeInSeconds) { director.time = timeInSeconds; director.Evaluate(); // 해당 시점으로 즉시 이동 }
void Start() { // 재생 완료 이벤트 director.stopped += OnCutsceneFinished; }
void OnCutsceneFinished(PlayableDirector d) { Debug.Log("컷씬 종료"); GameManager.Instance.OnCutsceneComplete(); }}2. Signal Track 설정
섹션 제목: “2. Signal Track 설정”에디터에서:
Timeline 창 → Add → Signal TrackSignal Track에서 우클릭 → Add Signal EmitterSignal Emitter 클릭 → Create Signal Asset (예: SpawnEnemySignal.asset)Signal Receiver 컴포넌트를 대상 오브젝트에 추가하고 Signal Asset과 콜백 메서드를 연결합니다.
3. Signal Receiver 코드 연동
섹션 제목: “3. Signal Receiver 코드 연동”using UnityEngine;
public class TimelineEventHandler : MonoBehaviour{ // Signal Receiver에서 연결할 메서드 (반드시 public void) public void OnSpawnEnemy() { EnemySpawner.Instance.SpawnWave(); Debug.Log("Signal: 적 스폰"); }
public void OnPlayExplosion() { VFXManager.Instance.PlayExplosion(transform.position); }
public void OnShowSubtitle() { SubtitleUI.Instance.Show("박사: 이 실험은 중단할 수 없어!"); }}4. C++에서 Signal 동적 발신
섹션 제목: “4. C++에서 Signal 동적 발신”using UnityEngine;using UnityEngine.Timeline;using UnityEngine.Playables;
public class DynamicSignalEmitter : MonoBehaviour{ [SerializeField] PlayableDirector director; [SerializeField] SignalAsset mySignal;
// 코드로 특정 시점에 Signal 발신 public void EmitSignalAtTime(double time) { // Signal Track의 Emitter를 직접 발신 // (주로 커스텀 Track에서 사용) var graph = director.playableGraph; if (graph.IsValid()) { // PlayableDirector를 통한 Signal 수동 트리거 director.time = time; director.Evaluate(); } }}5. 커스텀 Track — C# Playable Track
섹션 제목: “5. 커스텀 Track — C# Playable Track”using UnityEngine;using UnityEngine.Playables;using UnityEngine.Timeline;
// 클립 데이터[System.Serializable]public class SubtitleClip : PlayableAsset, ITimelineClipAsset{ public string text = ""; public ClipCaps clipCaps => ClipCaps.None;
public override Playable CreatePlayable(PlayableGraph graph, GameObject owner) { var playable = ScriptPlayable<SubtitleBehaviour>.Create(graph); playable.GetBehaviour().text = text; return playable; }}
// 클립 동작public class SubtitleBehaviour : PlayableBehaviour{ public string text;
public override void OnBehaviourPlay(Playable playable, FrameData info) { SubtitleUI.Instance?.Show(text); }
public override void OnBehaviourPause(Playable playable, FrameData info) { SubtitleUI.Instance?.Hide(); }}
// Track 정의[TrackColor(0.2f, 0.8f, 0.4f)][TrackClipType(typeof(SubtitleClip))]public class SubtitleTrack : TrackAsset { }6. Timeline과 Animator 연동
섹션 제목: “6. Timeline과 Animator 연동”// Animation Track의 바인딩을 런타임에 교체public void BindCharacter(GameObject character){ var timelineAsset = director.playableAsset as TimelineAsset;
foreach (var track in timelineAsset.GetOutputTracks()) { if (track is AnimationTrack animTrack) { director.SetGenericBinding( animTrack, character.GetComponent<Animator>()); } }}7. Timeline 재생 시간 동기화 UI
섹션 제목: “7. Timeline 재생 시간 동기화 UI”using UnityEngine;using UnityEngine.Playables;using UnityEngine.UI;
public class TimelineProgressUI : MonoBehaviour{ [SerializeField] PlayableDirector director; [SerializeField] Slider progressSlider;
void Update() { if (director.state == PlayState.Playing) { progressSlider.value = (float)(director.time / director.duration); } }
public void OnSliderChanged(float value) { director.time = value * director.duration; director.Evaluate(); }}Timeline은 Signal Track으로 게임 이벤트와 연결하고, 커스텀 Track으로 자막·UI 효과 등 게임 특화 기능을 확장할 수 있습니다. PlayableDirector.SetGenericBinding으로 런타임에 바인딩 대상을 교체하면 같은 타임라인을 여러 캐릭터에 재사용할 수 있습니다.