Unity Timeline Custom Track & Playable
Unity Timeline은 Playables API 위에 구축된 시퀀싱 시스템입니다. 기본 제공 트랙(Animation, Audio, Activation 등) 외에도 커스텀 트랙을 만들어 씬 연출, 게임플레이 이벤트, UI 애니메이션 등 어떤 동작도 Timeline에 통합할 수 있습니다.
Playables API 구조
섹션 제목: “Playables API 구조”PlayableGraph└── PlayableOutput (연결 대상: Animator, AudioSource 등) └── PlayableMixer (여러 클립 블렌딩) ├── PlayableBehaviour (클립 A) └── PlayableBehaviour (클립 B)커스텀 클립 데이터 정의
섹션 제목: “커스텀 클립 데이터 정의”using System;using UnityEngine;using UnityEngine.Playables;using UnityEngine.Timeline;
// 1. 클립 데이터 — Timeline에서 드래그하는 클립 에셋[Serializable]public class ColorFlashClip : PlayableAsset, ITimelineClipAsset { public Color flashColor = Color.white; public float intensity = 1f;
// 이 클립이 지원하는 기능 선언 public ClipCaps clipCaps => ClipCaps.Blending | ClipCaps.Extrapolation;
// 런타임 Playable 생성 public override Playable CreatePlayable(PlayableGraph graph, GameObject owner) { var playable = ScriptPlayable<ColorFlashBehaviour>.Create(graph); var behaviour = playable.GetBehaviour(); behaviour.flashColor = flashColor; behaviour.intensity = intensity; return playable; }}PlayableBehaviour 구현
섹션 제목: “PlayableBehaviour 구현”// 2. 런타임 동작 — 매 프레임 호출되는 실제 로직[Serializable]public class ColorFlashBehaviour : PlayableBehaviour { public Color flashColor; public float intensity;
// 그래프 재생 시작 public override void OnGraphStart(Playable playable) { Debug.Log("ColorFlash 그래프 시작"); }
// 클립 활성화 public override void OnBehaviourPlay(Playable playable, FrameData info) { Debug.Log("ColorFlash 클립 시작"); }
// 매 프레임 — weight: 현재 클립의 블렌딩 가중치 (0~1) public override void ProcessFrame(Playable playable, FrameData info, object playerData) { // playerData는 트랙 바인딩 오브젝트 var renderer = playerData as Renderer; if (renderer == null) return;
// 가중치(블렌딩) 반영 float weight = info.weight; var mat = renderer.material; mat.color = Color.Lerp(Color.white, flashColor * intensity, weight); }
// 클립 비활성화 public override void OnBehaviourPause(Playable playable, FrameData info) { // 원래 상태 복구 }}믹서(Mixer) 구현
섹션 제목: “믹서(Mixer) 구현”여러 클립이 겹칠 때 블렌딩 로직을 중앙에서 처리합니다.
// 3. 믹서 — 여러 클립의 가중치 합산public class ColorFlashMixerBehaviour : PlayableBehaviour { private Color _defaultColor; private bool _initialized;
public override void ProcessFrame(Playable playable, FrameData info, object playerData) { var renderer = playerData as Renderer; if (renderer == null) return;
// 처음 실행 시 기본 색상 저장 if (!_initialized) { _defaultColor = renderer.material.color; _initialized = true; }
Color blendedColor = Color.black; float totalWeight = 0f;
int inputCount = playable.GetInputCount(); for (int i = 0; i < inputCount; i++) { float weight = playable.GetInputWeight(i); if (weight <= 0f) continue;
var inputPlayable = (ScriptPlayable<ColorFlashBehaviour>)playable.GetInput(i); var behaviour = inputPlayable.GetBehaviour();
blendedColor += behaviour.flashColor * behaviour.intensity * weight; totalWeight += weight; }
// 블렌딩되지 않은 부분은 기본 색상 renderer.material.color = Color.Lerp(_defaultColor, blendedColor, Mathf.Clamp01(totalWeight)); }}커스텀 트랙 정의
섹션 제목: “커스텀 트랙 정의”using UnityEngine;using UnityEngine.Playables;using UnityEngine.Timeline;
// 4. 트랙 — Editor에서 보이는 트랙 타입[TrackColor(1f, 0.5f, 0f)] // 트랙 색상 (주황)[TrackClipType(typeof(ColorFlashClip))] // 허용 클립 타입[TrackBindingType(typeof(Renderer))] // 바인딩 오브젝트 타입public class ColorFlashTrack : TrackAsset { public override Playable CreateTrackMixer( PlayableGraph graph, GameObject go, int inputCount) { // 믹서 Playable 생성 return ScriptPlayable<ColorFlashMixerBehaviour>.Create(graph, inputCount); }}이 클래스를 만들면 Timeline 윈도우에서 Add Track > ColorFlashTrack이 나타납니다.
런타임 PlayableDirector 제어
섹션 제목: “런타임 PlayableDirector 제어”using UnityEngine;using UnityEngine.Playables;using UnityEngine.Timeline;
public class CutsceneController : MonoBehaviour { [SerializeField] private PlayableDirector _director; [SerializeField] private TimelineAsset _cutsceneTimeline; [SerializeField] private Renderer _targetRenderer;
void Start() { // 바인딩 교체 — 트랙에 실제 오브젝트 연결 var tracks = _cutsceneTimeline.GetOutputTracks(); foreach (var track in tracks) { if (track is ColorFlashTrack) { _director.SetGenericBinding(track, _targetRenderer); } } }
public void PlayCutscene() { _director.Play(_cutsceneTimeline); }
public void PauseCutscene() { _director.Pause(); }
public void SeekTo(double time) { _director.time = time; _director.Evaluate(); // 지정 시간 즉시 평가 }
// Timeline 이벤트 수신 void OnEnable() { _director.played += OnPlay; _director.stopped += OnStop; } void OnDisable() { _director.played -= OnPlay; _director.stopped -= OnStop; } void OnPlay(PlayableDirector d) => Debug.Log("컷씬 시작"); void OnStop(PlayableDirector d) => Debug.Log("컷씬 종료");}Signal 활용 (타임라인 이벤트)
섹션 제목: “Signal 활용 (타임라인 이벤트)”using UnityEngine;using UnityEngine.Timeline;
// Signal Emitter 수신 — Timeline에서 특정 시점에 이벤트 발생public class CutsceneSignalReceiver : MonoBehaviour, INotificationReceiver { public void OnNotify(Playable origin, INotification notification, object context) { if (notification is SignalEmitter emitter) { Debug.Log($"Signal 수신: {emitter.asset?.name}"); // Signal에 따른 로직 처리 } }}Timeline Signal은 Add Signal Emitter 버튼으로 마커를 추가하고, SignalReceiver 컴포넌트에서 UnityEvent로 연결하거나 위처럼 코드로 처리합니다.
핵심 요약
섹션 제목: “핵심 요약”| 클래스 | 역할 |
|---|---|
PlayableAsset | 클립 에셋 데이터, CreatePlayable 구현 |
PlayableBehaviour | 런타임 동작, ProcessFrame 구현 |
TrackAsset | 트랙 정의, CreateTrackMixer 구현 |
PlayableDirector | Timeline 재생/제어 |
INotificationReceiver | Signal 수신 |
커스텀 트랙은 애니메이션 이벤트, 포스트프로세싱 효과, 게임플레이 스크립트 등 어떤 로직도 Timeline에 통합할 수 있어 연출 시스템 설계의 핵심 도구입니다.