using System.Collections.Generic;
using UnityEngine.Playables;
namespace UnityEngine.Animations.Rigging
{
///
/// RigBuilder is the root component that holds the Rigs that create an Animation Rigging hierarchy.
/// Its purpose is to create the PlayableGraph that will be used in the associated Animator component to animate
/// a character with constraints.
///
[RequireComponent(typeof(Animator))]
[DisallowMultipleComponent, ExecuteInEditMode, AddComponentMenu("Animation Rigging/Setup/Rig Builder")]
[HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/RiggingWorkflow.html#rig-builder-component")]
public class RigBuilder : MonoBehaviour, IAnimationWindowPreview, IRigEffectorHolder
{
[SerializeField] private List m_RigLayers;
private IRigLayer[] m_RuntimeRigLayers;
private SyncSceneToStreamLayer m_SyncSceneToStreamLayer;
[SerializeField] private List m_Effectors = new List();
private bool m_IsInPreview;
#if UNITY_EDITOR
///
public IEnumerable effectors { get => m_Effectors; }
#endif
///
/// Delegate function that covers a RigBuilder calling OnEnable.
///
/// The RigBuilder component
public delegate void OnAddRigBuilderCallback(RigBuilder rigBuilder);
///
/// Delegate function that covers a RigBuilder calling OnDisable.
///
/// The RigBuilder component
public delegate void OnRemoveRigBuilderCallback(RigBuilder rigBuilder);
///
/// Notification callback that is sent whenever a RigBuilder calls OnEnable.
///
public static OnAddRigBuilderCallback onAddRigBuilder;
///
/// Notification callback that is sent whenever a RigBuilder calls OnDisable.
///
public static OnRemoveRigBuilderCallback onRemoveRigBuilder;
void OnEnable()
{
// Build runtime data.
if (Application.isPlaying)
Build();
onAddRigBuilder?.Invoke(this);
}
void OnDisable()
{
// Clear runtime data.
if (Application.isPlaying)
Clear();
onRemoveRigBuilder?.Invoke(this);
}
void OnDestroy()
{
Clear();
}
///
/// Updates the RigBuilder layers and evaluates the PlayableGraph manually.
///
/// The time in seconds by which to advance the RigBuilder PlayableGraph.
///
/// Manually evaluate the RigBuilder in LateUpdate.
///
///
public void Evaluate(float deltaTime)
{
if (!graph.IsValid())
return;
SyncLayers();
graph.Evaluate(deltaTime);
}
void Update()
{
if (!graph.IsValid())
return;
SyncLayers();
}
///
/// Synchronizes rigs and constraints with scene values.
/// This must be called before evaluating the PlayableGraph.
///
///
///
///
///
/// Synchronizing layers before evaluating a PlayableGraph created
/// outside the RigBuilder in LateUpdate.
///
///
public void SyncLayers()
{
if (m_RuntimeRigLayers == null)
return;
syncSceneToStreamLayer.Update(m_RuntimeRigLayers);
for (int i = 0, count = m_RuntimeRigLayers.Length; i < count; ++i)
{
if (m_RuntimeRigLayers[i].IsValid() && m_RuntimeRigLayers[i].active)
m_RuntimeRigLayers[i].Update();
}
}
///
/// Builds the RigBuilder PlayableGraph.
///
/// Returns true if the RigBuilder has created a valid PlayableGraph. Returns false otherwise.
public bool Build()
{
if (m_IsInPreview)
return false;
Clear();
var animator = GetComponent();
if (animator == null || layers.Count == 0)
return false;
// Make a copy of the layers list.
m_RuntimeRigLayers = layers.ToArray();
graph = RigBuilderUtils.BuildPlayableGraph(animator, m_RuntimeRigLayers, syncSceneToStreamLayer);
if (!graph.IsValid())
return false;
graph.Play();
return true;
}
///
/// Builds the RigBuilder playable nodes in an external PlayableGraph.
///
/// Destination PlayableGraph.
/// Returns true if the RigBuilder has created Playable nodes. Returns false otherwise.
public bool Build(PlayableGraph graph)
{
if (m_IsInPreview)
return false;
Clear();
var animator = GetComponent();
if (animator == null || layers.Count == 0)
return false;
// Make a copy of the layers list.
m_RuntimeRigLayers = layers.ToArray();
RigBuilderUtils.BuildPlayableGraph(graph, animator, m_RuntimeRigLayers, syncSceneToStreamLayer);
return true;
}
///
/// Destroys the RigBuilder PlayableGraph and frees associated RigLayers memory.
///
public void Clear()
{
if (m_IsInPreview)
return;
if (graph.IsValid())
graph.Destroy();
if (m_RuntimeRigLayers != null)
{
foreach (var layer in m_RuntimeRigLayers)
layer.Reset();
m_RuntimeRigLayers = null;
}
syncSceneToStreamLayer.Reset();
}
//
// IAnimationWindowPreview methods implementation
//
/// Notification callback when the animation previewer starts previewing an AnimationClip.
/// This is called by the Animation Window or the Timeline Editor.
public void StartPreview()
{
m_IsInPreview = true;
if (!enabled)
return;
// Make a copy of the layer list if it doesn't already exist.
if (m_RuntimeRigLayers == null)
m_RuntimeRigLayers = layers.ToArray();
var animator = GetComponent();
if (animator != null)
{
foreach (var layer in m_RuntimeRigLayers)
{
layer.Initialize(animator);
}
}
}
/// Notification callback when the animation previewer stops previewing an AnimationClip.
/// This is called by the Animation Window or the Timeline Editor.
public void StopPreview()
{
m_IsInPreview = false;
if (!enabled)
return;
if (Application.isPlaying)
return;
Clear();
}
/// Notification callback when the animation previewer updates its PlayableGraph before sampling an AnimationClip.
/// This is called by the Animation Window or the Timeline Editor.
/// The animation previewer PlayableGraph
public void UpdatePreviewGraph(PlayableGraph graph)
{
if (!enabled)
return;
if (!graph.IsValid() || m_RuntimeRigLayers == null)
return;
syncSceneToStreamLayer.Update(m_RuntimeRigLayers);
foreach (var layer in m_RuntimeRigLayers)
{
if (layer.IsValid() && layer.active)
layer.Update();
}
}
///
/// Appends custom Playable nodes to the animation previewer PlayableGraph.
///
/// The animation previewer PlayableGraph
/// The current root of the PlayableGraph
///
public Playable BuildPreviewGraph(PlayableGraph graph, Playable inputPlayable)
{
if (!enabled)
return inputPlayable;
if (m_RuntimeRigLayers == null)
StartPreview();
var animator = GetComponent();
if (animator == null || m_RuntimeRigLayers == null || m_RuntimeRigLayers.Length == 0)
return inputPlayable;
var playableChains = RigBuilderUtils.BuildPlayables(animator, graph, m_RuntimeRigLayers, syncSceneToStreamLayer);
foreach(var chain in playableChains)
{
if (chain.playables == null || chain.playables.Length == 0)
continue;
chain.playables[0].AddInput(inputPlayable, 0, 1);
inputPlayable = chain.playables[chain.playables.Length - 1];
}
return inputPlayable;
}
#if UNITY_EDITOR
///
public void AddEffector(Transform transform, RigEffectorData.Style style)
{
var effector = new RigEffectorData();
effector.Initialize(transform, style);
m_Effectors.Add(effector);
}
///
public void RemoveEffector(Transform transform)
{
m_Effectors.RemoveAll((RigEffectorData data) => data.transform == transform);
}
///
public bool ContainsEffector(Transform transform)
{
return m_Effectors.Exists((RigEffectorData data) => data.transform == transform);
}
#endif
///
/// Returns a list of RigLayer associated to this RigBuilder.
///
public List layers
{
get
{
if (m_RigLayers == null)
m_RigLayers = new List();
return m_RigLayers;
}
set => m_RigLayers = value;
}
private SyncSceneToStreamLayer syncSceneToStreamLayer
{
get
{
if (m_SyncSceneToStreamLayer == null)
m_SyncSceneToStreamLayer = new SyncSceneToStreamLayer();
return m_SyncSceneToStreamLayer;
}
set => m_SyncSceneToStreamLayer = value;
}
///
/// Retrieves the PlayableGraph created by this RigBuilder.
///
public PlayableGraph graph { get; private set; }
}
}