Important: The Playables API is included in Unity 5.2 as an “experimental” feature, which means that we do not guarantee that the API won’t change in future versions.
This new API provides a way to create tools, effects or other gameplay mechanisms by organizing and evaluating data sources in a tree-like structure.
This tree-like structure allows you to mix/blend/modify multiple data sources and play them through a single output. For example, in its current form, the Playable API supports animation graphs, providing the capacity to interact with Mecanim via scripting.
Even though the Playable API is currently limited to animation graphs, it is a generic API that could in the future be applied to Audio, Video, Scripts and custom Playables.
Note that in Unity 5.2, the Playable API is part of the “experimental” namespace, and that scripts created with it might not be compatible with future versions of Unity.
The following MonoBehaviour creates a simple tree with a single node, which plays a single clip.
Two new types of objects are introduced in this script: - The AnimationClipPlayable, which derives from the generic AnimationPlayable, and wraps an AnimationClip in order to make it compatible with the Playable API - a PlayableController which knows how to play graphs.
The Bind() operation connects them together by setting the AnimationClipPlayable as the root of the tree, and connecting the tree’s output to an “animation player”, which in this case is the Animator. The tree can then 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 = new AnimationClipPlayable(clip);
// Bind the playable to the player
GetComponent<Animator>().Play(clipPlayable);
}
}
We now introduce the AnimationMixerPlayable, which can blend two or more AnimationClipPlayables, or blend other mixers which themselves blend other clips, etc. The SetInputs() method will create the underlying AnimationClipPlayable nodes implicitly (the method can also take an array of AnimationPlayables 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 = new AnimationMixerPlayable();
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;
Debug.Log(weight);
m_Mixer.SetInputWeight(0, weight);
m_Mixer.SetInputWeight(1, 1 - weight);
}
}
Just like the AnimationClipPlayable wraps an AnimationClip, the AnimatorControllerPlayable can wrap an AnimatorController, allowing us to 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 = new AnimationClipPlayable(clip);
var controllerPlayable = new AnimatorControllerPlayable(animController);
var mixer = new AnimationMixerPlayable();
mixer.SetInputs(new AnimationPlayable[] {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, it is possible to explicitly set the local time of a Playable (local time will be propagated to children nodes). For instance, in this example, we 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 = 0.1f;
void Start ()
{
m_Root = new AnimationClipPlayable(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 ;
}
}
Similarly, the state of the tree, or a branch of the tree, can be set by changing the Playable.state parameter. The state will propagate to all children of the node, regardless of their previous state. (Keep in mind that if a child node was explicitly paused, setting a parent to Playing state will also set this child to Playing state).