using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using UnityEngine; using UnityEngine.Playables; [RequireComponent(typeof(Animator))] public class SimpleAnimation: MonoBehaviour { public interface State { bool enabled { get; set; } bool isValid { get; } float time { get; set; } float normalizedTime { get; set; } float speed { get; set; } string name { get; set; } float weight { get; set; } float length { get; } AnimationClip clip { get; } WrapMode wrapMode { get; set; } } public Animator animator { get { if (m_Animator == null) { m_Animator = GetComponent(); } return m_Animator; } } public bool animatePhysics { get { return m_AnimatePhysics; } set { m_AnimatePhysics = value; animator.updateMode = m_AnimatePhysics ? AnimatorUpdateMode.AnimatePhysics : AnimatorUpdateMode.Normal; } } public AnimatorCullingMode cullingMode { get { return animator.cullingMode; } set { m_CullingMode = value; animator.cullingMode = m_CullingMode; } } public bool isPlaying { get { return m_Playable.IsPlaying(); } } public bool playAutomatically { get { return m_PlayAutomatically; } set { m_PlayAutomatically = value; } } public AnimationClip clip { get { return m_Clip; } set { m_Clip = value; } } public WrapMode wrapMode { get { return m_WrapMode; } set { m_WrapMode = value; } } public void AddClip(AnimationClip clip, string newName) { AddState(clip, newName); } public void Blend(string stateName, float targetWeight, float fadeLength) { m_Animator.enabled = true; Kick(); m_Playable.Blend(stateName, targetWeight, fadeLength); } public void CrossFade(string stateName, float fadeLength) { m_Animator.enabled = true; Kick(); m_Playable.Crossfade(stateName, fadeLength); } public void CrossFadeQueued(string stateName, float fadeLength, QueueMode queueMode) { m_Animator.enabled = true; Kick(); m_Playable.CrossfadeQueued(stateName, fadeLength, queueMode); } public int GetClipCount() { return m_Playable.GetClipCount(); } public bool IsPlaying(string stateName) { return m_Playable.IsPlaying(stateName); } public void Stop() { m_Playable.StopAll(); } public void Stop(string stateName) { m_Playable.Stop(stateName); } public void Sample() { m_Graph.Evaluate(); } public bool Play() { m_Animator.enabled = true; Kick(); if (m_Clip != null && m_PlayAutomatically) { m_Playable.Play(kDefaultStateName); } return false; } public void AddState(AnimationClip clip, string name) { Kick(); if (m_Playable.AddClip(clip, name)) { RebuildStates(); } } public void RemoveState(string name) { if (m_Playable.RemoveClip(name)) { RebuildStates(); } } public bool Play(string stateName) { //Debug.Log("playing " + stateName); m_Animator.enabled = true; Kick(); return m_Playable.Play(stateName); } public void PlayQueued(string stateName, QueueMode queueMode) { m_Animator.enabled = true; Kick(); m_Playable.PlayQueued(stateName, queueMode); } public void RemoveClip(AnimationClip clip) { if (clip == null) throw new System.NullReferenceException("clip"); if ( m_Playable.RemoveClip(clip) ) { RebuildStates(); } } public void Unwind() { Kick(); m_Playable.Unwind(); } public void Rewind() { Kick(); m_Playable.Rewind(); } public void Rewind(string stateName) { Kick(); m_Playable.Rewind(stateName); } public State GetState(string stateName) { SimpleAnimationPlayable.IState state = m_Playable.GetState(stateName); if (state == null) return null; return new StateImpl(state, this); } public IEnumerable GetStates() { return new StateEnumerable(this); } public State this[string name] { get { return GetState(name); } } const string kDefaultStateName = "Default"; private class StateEnumerable : IEnumerable { private SimpleAnimation m_Owner; public StateEnumerable(SimpleAnimation owner) { m_Owner = owner; } public IEnumerator GetEnumerator() { return new StateEnumerator(m_Owner); } IEnumerator IEnumerable.GetEnumerator() { return new StateEnumerator(m_Owner); } class StateEnumerator : IEnumerator { private SimpleAnimation m_Owner; private IEnumerator m_Impl; public StateEnumerator(SimpleAnimation owner) { m_Owner = owner; m_Impl = m_Owner.m_Playable.GetStates().GetEnumerator(); Reset(); } State GetCurrent() { return new StateImpl(m_Impl.Current, m_Owner); } object IEnumerator.Current { get { return GetCurrent(); } } State IEnumerator.Current { get { return GetCurrent(); } } public void Dispose() { } public bool MoveNext() { return m_Impl.MoveNext(); } public void Reset() { m_Impl.Reset(); } } } private class StateImpl : State { public StateImpl(SimpleAnimationPlayable.IState handle, SimpleAnimation component) { m_StateHandle = handle; m_Component = component; } private SimpleAnimationPlayable.IState m_StateHandle; private SimpleAnimation m_Component; bool State.enabled { get { return m_StateHandle.enabled; } set { m_StateHandle.enabled = value; if (value) { m_Component.Kick(); } } } bool State.isValid { get { return m_StateHandle.IsValid(); } } float State.time { get { return m_StateHandle.time; } set { m_StateHandle.time = value; m_Component.Kick(); } } float State.normalizedTime { get { return m_StateHandle.normalizedTime; } set { m_StateHandle.normalizedTime = value; m_Component.Kick(); } } float State.speed { get { return m_StateHandle.speed; } set { m_StateHandle.speed = value; m_Component.Kick(); } } string State.name { get { return m_StateHandle.name; } set { m_StateHandle.name = value; } } float State.weight { get { return m_StateHandle.weight; } set { m_StateHandle.weight = value; m_Component.Kick(); } } float State.length { get { return m_StateHandle.length; } } AnimationClip State.clip { get { return m_StateHandle.clip; } } WrapMode State.wrapMode { get { return m_StateHandle.wrapMode; } set { Debug.LogError("Not Implemented"); } } } [System.Serializable] public class EditorState { public AnimationClip clip; public string name; public bool defaultState; } protected void Kick() { if (!m_IsPlaying) { m_Graph.Play(); m_IsPlaying = true; } } protected PlayableGraph m_Graph; protected PlayableHandle m_LayerMixer; protected PlayableHandle m_TransitionMixer; protected Animator m_Animator; protected bool m_Initialized; protected bool m_IsPlaying; protected SimpleAnimationPlayable m_Playable; [SerializeField] protected bool m_PlayAutomatically = true; [SerializeField] protected bool m_AnimatePhysics = false; [SerializeField] protected AnimatorCullingMode m_CullingMode = AnimatorCullingMode.CullUpdateTransforms; [SerializeField] protected WrapMode m_WrapMode; [SerializeField] protected AnimationClip m_Clip; [SerializeField] private EditorState[] m_States; protected virtual void OnEnable() { Initialize(); m_Graph.Play(); if (m_PlayAutomatically) { Stop(); Play(); } } protected virtual void OnDisable() { if (m_Initialized) { Stop(); m_Graph.Stop(); } } private void Reset() { if (m_Graph.IsValid()) m_Graph.Destroy(); m_Initialized = false; } private void Initialize() { if (m_Initialized) return; m_Animator = GetComponent(); m_Animator.updateMode = m_AnimatePhysics ? AnimatorUpdateMode.AnimatePhysics : AnimatorUpdateMode.Normal; m_Animator.cullingMode = m_CullingMode; m_Graph = PlayableGraph.Create(); m_Graph.SetTimeUpdateMode(DirectorUpdateMode.GameTime); SimpleAnimationPlayable template = new SimpleAnimationPlayable(); var playable = ScriptPlayable.Create(m_Graph, template, 1); m_Playable = playable.GetBehaviour(); m_Playable.onDone += OnPlayableDone; if (m_States == null) { m_States = new EditorState[1]; m_States[0] = new EditorState(); m_States[0].defaultState = true; m_States[0].name = "Default"; } if (m_States != null) { foreach (var state in m_States) { if (state.clip) { m_Playable.AddClip(state.clip, state.name); } } } EnsureDefaultStateExists(); AnimationPlayableUtilities.Play(m_Animator, m_Playable.playable, m_Graph); Play(); Kick(); m_Initialized = true; } private void EnsureDefaultStateExists() { if (m_Playable != null && m_Clip != null && m_Playable.GetState(kDefaultStateName) == null) { m_Playable.AddClip(m_Clip, kDefaultStateName); Kick(); } } protected virtual void Awake() { Initialize(); } protected void OnDestroy() { if (m_Graph.IsValid()) { m_Graph.Destroy(); } } private void OnPlayableDone() { m_Graph.Stop(); m_IsPlaying = false; } private void RebuildStates() { var playableStates = GetStates(); var list = new List(); foreach (var state in playableStates) { var newState = new EditorState(); newState.clip = state.clip; newState.name = state.name; list.Add(newState); } m_States = list.ToArray(); } }