using System; using UnityEngine; using UnityEngine.Animations; using UnityEngine.Assertions; using UnityEngine.Playables; using Logger = RebootKit.Engine.Foundation.Logger; // @TODO: // - avoid boxing values // - think about refactoring in order to reduce gc allocs namespace RebootKit.Engine.Animations { public interface IReAnimatorNode { string Name { get; } void Tick(float deltaTime); IPlayable Build(PlayableGraph graph); bool TryFindChild(string name, out IReAnimatorNode node); } [Serializable] class ReAnimatorLayer { public string name; public AvatarMask mask; public bool isAdditive; [Range(0.0f, 1.0f)] public float weight = 1.0f; [SerializeReference] public IReAnimatorNode root; } [DefaultExecutionOrder(-100)] public class ReAnimator : MonoBehaviour { static readonly Logger s_Logger = new Logger(nameof(ReAnimator)); [SerializeField] Animator m_Animator; [SerializeField] ReAnimatorLayer[] m_Layers; PlayableGraph m_Graph; AnimationPlayableOutput m_Output; AnimationLayerMixerPlayable m_LayerMixer; void Awake() { Assert.IsNotNull(m_Animator, "Animator is not set"); Assert.IsTrue(m_Layers.Length > 0, "Atleast one layer must be defined!"); m_Animator.runtimeAnimatorController = null; m_Graph = PlayableGraph.Create(name); m_Output = AnimationPlayableOutput.Create(m_Graph, "Animation Output", m_Animator); m_LayerMixer = AnimationLayerMixerPlayable.Create(m_Graph, m_Layers.Length); m_Output.SetSourcePlayable(m_LayerMixer, 0); for (int i = 0; i < m_Layers.Length; ++i) { IPlayable playable = m_Layers[i].root.Build(m_Graph); if (playable is AnimationMixerPlayable mixerPlayable) { m_LayerMixer.ConnectInput(i, mixerPlayable, 0, m_Layers[i].weight); } else if (playable is AnimationClipPlayable clipPlayable) { m_LayerMixer.ConnectInput(i, clipPlayable, 0, m_Layers[i].weight); } m_LayerMixer.SetLayerAdditive((uint)i, m_Layers[i].isAdditive); if (m_Layers[i].mask != null) { m_LayerMixer.SetLayerMaskFromAvatarMask((uint)i, m_Layers[i].mask); } } m_Graph.Play(); } void OnDestroy() { if (m_Graph.IsValid()) { m_Graph.Destroy(); } } void Update() { for (int i = 0; i < m_Layers.Length; i++) { m_Layers[i].root.Tick(Time.deltaTime); } } public void SetLayerWeight(int layer, float weight) { m_Layers[layer].weight = weight; m_LayerMixer.SetInputWeight(layer, weight); } public IReAnimatorNode FindNode(string nodeName) { foreach (ReAnimatorLayer layer in m_Layers) { if (layer.root.Name.Equals(nodeName, StringComparison.Ordinal)) { return layer.root; } if (layer.root.TryFindChild(nodeName, out IReAnimatorNode childNode)) { return childNode; } } s_Logger.Error($"Couldn't find node with name: {nodeName}"); return null; } public TNode FindNode(string nodeName) where TNode : class, IReAnimatorNode { IReAnimatorNode node = FindNode(nodeName); if (node is TNode tnode) { return tnode; } s_Logger.Error($"Couldn't find node with name: {nodeName} of type {typeof(TNode).Name}"); return null; } } }