콘텐츠로 이동

Unity Timeline Custom Track & Playable

Unity Timeline은 Playables API 위에 구축된 시퀀싱 시스템입니다. 기본 제공 트랙(Animation, Audio, Activation 등) 외에도 커스텀 트랙을 만들어 씬 연출, 게임플레이 이벤트, UI 애니메이션 등 어떤 동작도 Timeline에 통합할 수 있습니다.


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;
}
}

// 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) {
// 원래 상태 복구
}
}

여러 클립이 겹칠 때 블렌딩 로직을 중앙에서 처리합니다.

// 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이 나타납니다.


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("컷씬 종료");
}

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 구현
PlayableDirectorTimeline 재생/제어
INotificationReceiverSignal 수신

커스텀 트랙은 애니메이션 이벤트, 포스트프로세싱 효과, 게임플레이 스크립트 등 어떤 로직도 Timeline에 통합할 수 있어 연출 시스템 설계의 핵심 도구입니다.