The Unity Script Reference (API) provides a way to create tools, effects and other gameplay mechanisms by organizing and evaluating data sources in a tree-like structure. This tree-like structure lets you mix, blend and modify multiple data sources and play them through a single output. The Playable API supports animation graphs, so you can interact with animation via scripting.
Note that the Playable API is part of the “experimental” namespace, and that scripts created with it might not be compatible with future versions of Unity.
If you have used Playable API in versions before 5.4 you should check the Upgrade Guide for details of migrating to 5.4.
The following MonoBehaviour creates a simple tree with a single node, which plays a single clip.
The AnimationClipPlayable
wraps an AnimationClip
to make it compatible with the Playable API.
The Play()
operation connects the Playable with the Animator so the root of the graph (in this case the AnimationClipPlayable
) can be played.
using UnityEngine;
using UnityEngine.Experimental.Director;
[RequireComponent (typeof (Animator))]
public class PlayAnimation : MonoBehaviour
{
public AnimationClip clip;
void Start ()
{
// Wrap the clip in a playable
var clipPlayable = AnimationClipPlayable.Create(clip);
// Bind the playable to the player
GetComponent<Animator>().Play(clipPlayable);
}
}
The AnimationMixerPlayable
blends two or more AnimationClipPlayable
s, or can blend other mixers, which themselves blend other clips, etc. The SetInputs()
method creates the underlying AnimationClipPlayable
nodes implicitly (this method can also take an array of AnimationPlayable
s directly).
The weight of each clip in the blend is adjusted dynamically via SetInputWeight()
, creating a blend tree that smoothly changes from one animation to the other over time.
using UnityEngine;
using UnityEngine.Experimental.Director;
[RequireComponent (typeof (Animator))]
public class MixAnimation : MonoBehaviour
{
public AnimationClip clip1;
public AnimationClip clip2;
private AnimationMixerPlayable m_Mixer;
void Start ()
{
// Create the mixer and connect it to clips
// (AnimationClipPlayables are created implicitly)
m_Mixer = AnimationMixerPlayable.Create();
m_Mixer.SetInputs(new[] {clip1, clip2});
// Bind the playable graph to the player
GetComponent<Animator>().Play(m_Mixer);
}
void Update()
{
// Adjust the weight of each clip in the blend tree based on time
float weight = (Time.time % 10) / 10;
m_Mixer.SetInputWeight(0, weight);
m_Mixer.SetInputWeight(1, 1 - weight);
}
}
Just as the AnimationClipPlayable wraps an AnimationClip, the AnimatorControllerPlayable
wraps an AnimatorController
so you can blend them with other AnimationPlayables.
using UnityEngine;
using UnityEngine.Experimental.Director;
[RequireComponent (typeof (Animator))]
public class BlendAnimatorController : MonoBehaviour
{
public AnimationClip clip;
public RuntimeAnimatorController animController;
void Start ()
{
// Wrap the clip and the controller in playables
var clipPlayable = AnimationClipPlayable.Create(clip);
var controllerPlayable = AnimatorControllerPlayable.Create(animController);
var mixer = AnimationMixerPlayable.Create();
mixer.SetInputs(new Playable[] {clipPlayable, controllerPlayable});
// Bind the playable graph to the player
GetComponent<Animator>().Play(mixer);
}
}
By default, the Play()
method on the PlayableController
handles all the timing of the tree playback. However, for additional control, you can explicitly set the local time of a Playable
. This local time will be passed on to child nodes.
In this example you can pause the playback, and advance and rewind the animation manually using the keyboard arrows.
using UnityEngine;
using UnityEngine.Experimental.Director;
[RequireComponent (typeof (Animator))]
public class PlayWithTimeControl : MonoBehaviour
{
public AnimationClip clip;
private Playable m_Root;
private const float k_SpeedFactor = 1f;
void Start ()
{
m_Root = AnimationClipPlayable.Create(clip);
// Bind the playable to the player
GetComponent<Animator>().Play(m_Root);
m_Root.state = PlayState.Paused;
}
void Update ()
{
// Control the time manually based on the input
float horizontalInput = Input.GetAxis("Horizontal");
m_Root.time += horizontalInput * k_SpeedFactor * Time.deltaTime;
}
}
You can set the state of the tree, or a branch of the tree, by changing the Playable.state
parameter. This state will pass to all children of the node, regardless of their previous state. If a child node was explicitly paused, setting a parent to the Playing state will also set the child to the Playing state.