이 문서의 모든 예제에서는 PlayableGraph Visualizer(아래 사진 참조)를 사용하여 플레이어블 API로 생성된 트리와 노드를 도해로 설명합니다. PlayableGraph Visualizer는 GitHub를 통해 제공되는 도구입니다.
PlayableGraph Visualizer를 사용하는 방법은 다음과 같습니다.
사용 중인 Unity 버전에 해당하는 PlayableGraph Visualizer를 GitHub 저장소에서 다운로드합니다.
Window > PlayableGraph Visualizer 를 선택하여 도구를 엽니다.
GraphVisualizerClient.Show(PlayableGraph 그래프, 문자열 이름)를 사용하여 그래프를 등록합니다.
그래프에서 플레이어블은 컬러 노드로 표시됩니다. 연결선의 컬러 채도는 블렌딩 가중치를 나타냅니다. 이 도구에 대한 자세한 내용은 GitHub를 참조하십시오.
다음는 플레이어블 노드 하나에 연결된 플레이어블 출력 하나가 있는 단순 PlayableGraph
의 예제입니다. 플레이어블 노드는 애니메이션 클립 하나를 재생합니다. 애니메이션 클립을 플레이어블 API와 호환되게 하려면 AnimationClipPlayable
으로 래핑(wrapping)해야 합니다.
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
[RequireComponent(typeof(Animator))]
public class PlayAnimationSample : MonoBehaviour
{
public AnimationClip clip;
PlayableGraph playableGraph;
void Start()
{
playableGraph = PlayableGraph.Create();
playableGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
// Wrap the clip in a playable
var clipPlayable = AnimationClipPlayable.Create(playableGraph, clip);
// Connect the Playable to an output
playableOutput.SetSourcePlayable(clipPlayable);
// Plays the Graph.
playableGraph.Play();
}
void OnDisable()
{
// Destroys all Playables and PlayableOutputs created by the graph.
playableGraph.Destroy();
}
}
아래 예제와 같이 AnimationPlayableUtilities
를 사용하여 애니메이션 플레이어블의 생성과 재생을 간소화할 수 있습니다.
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
[RequireComponent(typeof(Animator))]
public class PlayAnimationUtilitiesSample : MonoBehaviour
{
public AnimationClip clip;
PlayableGraph playableGraph;
void Start()
{
AnimationPlayableUtilities.PlayClip(GetComponent<Animator>(), clip, out playableGraph);
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}
다음은 AnimationMixerPlayable
을 사용하여 애니메이션 클립 두 개를 블렌드하는 예제입니다. 애니메이션 클립을 블렌드하기 전에 플레이어블로 래핑해야 합니다. 그러기 위해서는 AnimationClipPlayable
(clipPlayable0 및 clipPlayable1)이 각 AnimationClip
(clip0 및 clip1)을 래핑해야 합니다. SetInputWeight()
메서드는 각 플레이어블의 블렌드 가중치를 동적으로 조절합니다.
아래 예제에는 나와 있지 않지만, AnimationMixerPlayable
을 사용하여 플레이어블 믹서와 기타 플레이어블을 블렌드할 수도 있습니다.
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
[RequireComponent(typeof(Animator))]
public class MixAnimationSample : MonoBehaviour
{
public AnimationClip clip0;
public AnimationClip clip1;
public float weight;
PlayableGraph playableGraph;
AnimationMixerPlayable mixerPlayable;
void Start()
{
// Creates the graph, the mixer and binds them to the Animator.
playableGraph = PlayableGraph.Create();
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
mixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2);
playableOutput.SetSourcePlayable(mixerPlayable);
// Creates AnimationClipPlayable and connects them to the mixer.
var clipPlayable0 = AnimationClipPlayable.Create(playableGraph, clip0);
var clipPlayable1 = AnimationClipPlayable.Create(playableGraph, clip1);
playableGraph.Connect(clipPlayable0, 0, mixerPlayable, 0);
playableGraph.Connect(clipPlayable1, 0, mixerPlayable, 1);
// Plays the Graph.
playableGraph.Play();
}
void Update()
{
weight = Mathf.Clamp01(weight);
mixerPlayable.SetInputWeight(0, 1.0f-weight);
mixerPlayable.SetInputWeight(1, weight);
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}
다음은 AnimationMixerPlayable
을 사용하여 AnimationClip
을 AnimatorController
와 블렌드하는 방법을 보여주는 예제입니다.
AnimationClip
과 AnimatorController
를 블렌드하기 전에 플레이어블로 래핑해야 합니다. 그러기 위해서는 AnimationClipPlayable
(clipPlayable)이 AnimationClip
(clip)을 래핑하고 AnimatorControllerPlayable
(ctrlPlayable)이 RuntimeAnimatorController(controller)를 래핑해야 합니다. SetInputWeight(
) 메서드는 각 플레이어블의 블렌드 가중치를 동적으로 조절합니다.
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
[RequireComponent(typeof(Animator))]
public class RuntimeControllerSample : MonoBehaviour
{
public AnimationClip clip;
public RuntimeAnimatorController controller;
public float weight;
PlayableGraph playableGraph;
AnimationMixerPlayable mixerPlayable;
void Start()
{
// Creates the graph, the mixer and binds them to the Animator.
playableGraph = PlayableGraph.Create();
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
mixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2);
playableOutput.SetSourcePlayable(mixerPlayable);
// Creates AnimationClipPlayable and connects them to the mixer.
var clipPlayable = AnimationClipPlayable.Create(playableGraph, clip);
var ctrlPlayable = AnimatorControllerPlayable.Create(playableGraph, controller);
playableGraph.Connect(clipPlayable, 0, mixerPlayable, 0);
playableGraph.Connect(ctrlPlayable, 0, mixerPlayable, 1);
// Plays the Graph.
playableGraph.Play();
}
void Update()
{
weight = Mathf.Clamp01(weight);
mixerPlayable.SetInputWeight(0, 1.0f-weight);
mixerPlayable.SetInputWeight(1, weight);
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}
다음은 두 가지 플레이어블 출력 타입인 AudioPlayableOutput
과 AnimationPlayableOutput
이 있는 PlayableGraph
를 생성하는 방법을 보여주는 예제입니다. PlayableGraph
에는 타입이 다른 여러 플레이어블 출력이 포함될 수 있습니다.
아래 예제에서 AudioPlayableOutput
에 연결된 AudioClipPlayable
을 통해 AudioClip
을 재생하는 방법도 확인할 수 있습니다.
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Audio;
using UnityEngine.Playables;
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(AudioSource))]
public class MultiOutputSample : MonoBehaviour
{
public AnimationClip animationClip;
public AudioClip audioClip;
PlayableGraph playableGraph;
void Start()
{
playableGraph = PlayableGraph.Create();
// Create the outputs.
var animationOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
var audioOutput = AudioPlayableOutput.Create(playableGraph, "Audio", GetComponent<AudioSource>());
// Create the playables.
var animationClipPlayable = AnimationClipPlayable.Create(playableGraph, animationClip);
var audioClipPlayable = AudioClipPlayable.Create(playableGraph, audioClip, true);
// Connect the playables to an output
animationOutput.SetSourcePlayable(animationClipPlayable);
audioOutput.SetSourcePlayable(audioClipPlayable);
// Plays the Graph.
playableGraph.Play();
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}
다음은 Playable.SetPlayState()
메서드를 사용하여 PlayableGraph
트리의 노드 재생 상태를 제어하는 방법을 보여주는 예제입니다. SetPlayState
메서드는 전체 트리, 트리의 브랜치 하나, 또는 노드 하나의 재생 상태를 제어합니다.
노드의 재생 상태를 설정하면 자식의 재생 상태에 관계없이 노드의 모든 자식에게 상태가 전이됩니다. 예를 들어 자식 노드가 명백하게 일지 정지되어 있더라도 부모 노드를 “playing”으로 설정하면 모든 자식 노드도 “playing”으로 설정됩니다.
아래 예제에서 PlayableGraph
에는 두 애니메이션 클립을 블렌드하는 믹서가 포함되어 있습니다. AnimationClipPlayable
은 각 애니메이션 클립을 래핑하고 SetPlayState()
메서드는 두 번째 플레이어블을 명백하게 일시 정지합니다. 두 번째 AnimationClipPlayable
은 명백하게 일시 정지되었으므로 내부 시간이 진행되지 않고 같은 값을 출력합니다. 정확한 값은 AnimationClipPlayable
이 일시 정지된 특정 시간에 따라 다릅니다.
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
[RequireComponent(typeof(Animator))]
public class PauseSubGraphAnimationSample : MonoBehaviour
{
public AnimationClip clip0;
public AnimationClip clip1;
PlayableGraph playableGraph;
AnimationMixerPlayable mixerPlayable;
void Start()
{
// Creates the graph, the mixer and binds them to the Animator.
playableGraph = PlayableGraph.Create();
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
mixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2);
playableOutput.SetSourcePlayable(mixerPlayable);
// Creates AnimationClipPlayable and connects them to the mixer.
var clipPlayable0 = AnimationClipPlayable.Create(playableGraph, clip0);
var clipPlayable1 = AnimationClipPlayable.Create(playableGraph, clip1);
playableGraph.Connect(clipPlayable0, 0, mixerPlayable, 0);
playableGraph.Connect(clipPlayable1, 0, mixerPlayable, 1);
mixerPlayable.SetInputWeight(0, 1.0f);
mixerPlayable.SetInputWeight(1, 1.0f);
clipPlayable1.SetPlayState(PlayState.Paused);
// Plays the Graph.
playableGraph.Play();
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}
다음은 Play() 메서드를 사용하여 PlayableGraph를 재생하고, SetPlayState() 메서드를 사용하여 플레이어블을 일시 정지하고, SetTime() 메서드를 사용하여 플레이어블의 로컬 시간을 변수를 통해 수동으로 설정하는 방법을 보여주는 예제입니다.
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
[RequireComponent(typeof(Animator))]
public class PlayWithTimeControlSample : MonoBehaviour
{
public AnimationClip clip;
public float time;
PlayableGraph playableGraph;
AnimationClipPlayable playableClip;
void Start()
{
playableGraph = PlayableGraph.Create();
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
// Wrap the clip in a playable
playableClip = AnimationClipPlayable.Create(playableGraph, clip);
// Connect the Playable to an output
playableOutput.SetSourcePlayable(playableClip);
// Plays the Graph.
playableGraph.Play();
// Stops time from progressing automatically.
playableClip.SetPlayState(PlayState.Paused);
}
void Update ()
{
// Control the time manually
playableClip.SetTime(time);
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}
다음은 PlayableBehaviour
공용 클래스를 사용하여 커스텀 플레이어블을 생성하는 방법을 보여주는 예제입니다. 아래 예제에서 PrepareFrame()
가상 메서드를 오버라이드하여 PlayableGraph
의 노드를 제어하는 방법도 확인할 수 있습니다. 커스텀 플레이어블은 PlayableBehaviour
클래스의 다른 가상 메서드를 오버라이드할 수 있습니다.
아래 예제에서 제어되는 노드는 일련의 애니메이션 클립(clipsToPlay)입니다. SetInputMethod()
는 각 애니메이션 클립의 블렌드 가중치를 수정하여 클립이 한 번에 하나씩만 재생되도록 하고, SetTime()
메서드는 로컬 시간을 조정하여 애니메이션 클립이 활성화되는 순간에 재생이 시작되도록 합니다.
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
public class PlayQueuePlayable : PlayableBehaviour
{
private int m_CurrentClipIndex = -1;
private float m_TimeToNextClip;
private Playable mixer;
public void Initialize(AnimationClip[] clipsToPlay, Playable owner, PlayableGraph graph)
{
owner.SetInputCount(1);
mixer = AnimationMixerPlayable.Create(graph, clipsToPlay.Length);
graph.Connect(mixer, 0, owner, 0);
owner.SetInputWeight(0, 1);
for (int clipIndex = 0 ; clipIndex < mixer.GetInputCount() ; ++clipIndex)
{
graph.Connect(AnimationClipPlayable.Create(graph, clipsToPlay[clipIndex]), 0, mixer, clipIndex);
mixer.SetInputWeight(clipIndex, 1.0f);
}
}
override public void PrepareFrame(Playable owner, FrameData info)
{
if (mixer.GetInputCount() == 0)
return;
// Advance to next clip if necessary
m_TimeToNextClip -= (float)info.deltaTime;
if (m_TimeToNextClip <= 0.0f)
{
m_CurrentClipIndex++;
if (m_CurrentClipIndex >= mixer.GetInputCount())
m_CurrentClipIndex = 0;
var currentClip = (AnimationClipPlayable)mixer.GetInput(m_CurrentClipIndex);
// Reset the time so that the next clip starts at the correct position
currentClip.SetTime(0);
m_TimeToNextClip = currentClip.GetAnimationClip().length;
}
// Adjust the weight of the inputs
for (int clipIndex = 0 ; clipIndex < mixer.GetInputCount(); ++clipIndex)
{
if (clipIndex == m_CurrentClipIndex)
mixer.SetInputWeight(clipIndex, 1.0f);
else
mixer.SetInputWeight(clipIndex, 0.0f);
}
}
}
[RequireComponent(typeof (Animator))]
public class PlayQueueSample : MonoBehaviour
{
public AnimationClip[] clipsToPlay;
PlayableGraph playableGraph;
void Start()
{
playableGraph = PlayableGraph.Create();
var playQueuePlayable = ScriptPlayable<PlayQueuePlayable>.Create(playableGraph);
var playQueue = playQueuePlayable.GetBehaviour();
playQueue.Initialize(clipsToPlay, playQueuePlayable, playableGraph);
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
playableOutput.SetSourcePlayable(playQueuePlayable);
playableOutput.SetSourceInputPort(0);
playableGraph.Play();
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}