116 lines
3.8 KiB
C#
116 lines
3.8 KiB
C#
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<TNode>(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;
|
|
}
|
|
}
|
|
} |