/************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #region Public API /************************************************************************************************************************/ /// <summary> /// Constructs a new <see cref="ControllerState"/> to play the `animatorController` without connecting /// it to the <see cref="PlayableGraph"/>. /// </summary> protected ControllerState(AnimancerPlayable root, RuntimeAnimatorController animatorController, bool keepStateOnStop = false) : base(root) { KeepStateOnStop = keepStateOnStop; CreatePlayable(animatorController); }
/************************************************************************************************************************/ /// <summary> /// Invokes the <see cref="AnimancerEvent.Sequence.OnEnd"/> callback of the state that is playing the animation /// which triggered the event. Returns true if such a state exists (even if it doesn't have a callback). /// </summary> private static bool OnEndEventReceived(AnimancerPlayable animancer, AnimationEvent animationEvent) { var layers = animancer.Layers; var count = layers.Count; // Try targeting the current state on each layer first. for (int i = 0; i < count; i++) { if (TryInvokeOnEndEventRecursive(layers[i].CurrentState, animationEvent)) { return(true); } } // Otherwise try every state. for (int i = 0; i < count; i++) { if (TryInvokeOnEndEventRecursive(layers[i], animationEvent)) { return(true); } } return(false); }
/************************************************************************************************************************/ /// <summary>Creates a new <see cref="AnimancerPlayable"/> if it doesn't already exist.</summary> private void InitialisePlayable() { if (IsPlayableInitialised) { return; } #if UNITY_EDITOR var currentEvent = Event.current; if (currentEvent != null && (currentEvent.type == EventType.Layout || currentEvent.type == EventType.Repaint)) { Debug.LogWarning("Creating an AnimancerPlayable during a " + currentEvent.type + " event is likely undesirable."); } #endif if (_Animator == null) { _Animator = GetComponent <Animator>(); } AnimancerPlayable.SetNextGraphName(name + ".Animancer"); _Playable = AnimancerPlayable.Create(); _Playable.SetOutput(_Animator, this); #if UNITY_EDITOR if (_Animator != null) { InitialUpdateMode = UpdateMode; } #endif }
/************************************************************************************************************************/ /// <summary> /// Constructs a new <see cref="Float1ControllerState"/> to play the `controller` without connecting /// it to the <see cref="PlayableGraph"/>. /// </summary> private Float1ControllerState(AnimancerPlayable root, RuntimeAnimatorController controller, Parameter parameter, bool resetStatesOnStop = true) : base(root, controller, resetStatesOnStop) { _Parameter = parameter; _Parameter.ValidateHasParameter(controller, AnimatorControllerParameterType.Float); }
/************************************************************************************************************************/ /// <summary>[Internal] Creates a new <see cref="LayerList"/>.</summary> internal LayerList(AnimancerPlayable root, out Playable layerMixer) { Root = root; _Layers = new AnimancerLayer[DefaultCapacity]; layerMixer = LayerMixer = AnimationLayerMixerPlayable.Create(root._Graph, 1); Root._Graph.Connect(layerMixer, 0, Root._RootPlayable, 0); }
/************************************************************************************************************************/ /// <summary> /// Called by Unity when this component is destroyed. /// Ensures that the <see cref="Playable"/> is properly cleaned up. /// </summary> protected virtual void OnDestroy() { if (IsPlayableInitialised) { _Playable.Destroy(); _Playable = null; } }
/************************************************************************************************************************/ #region Graph /************************************************************************************************************************/ /// <summary>The <see cref="AnimancerPlayable"/> at the root of the graph.</summary> public void SetRoot(AnimancerPlayable root) { if (Root == root) { return; } if (Root != null) { Root.CancelPreUpdate(this); Root.States.Unregister(this); if (_EventDispatcher != null) { Root.CancelPostUpdate(_EventDispatcher); } if (_Parent != null) { _Parent.OnRemoveChild(this); _Parent = null; } Index = -1; DestroyPlayable(); } Root = root; if (root != null) { #if UNITY_ASSERTIONS GC.SuppressFinalize(this); #endif root.States.Register(this); if (_EventDispatcher != null) { root.RequirePostUpdate(_EventDispatcher); } CreatePlayable(); } for (int i = ChildCount - 1; i >= 0; i--) { GetChild(i)?.SetRoot(root); } if (_Parent != null) { CopyIKFlags(_Parent); } }
/************************************************************************************************************************/ /// <summary>Constructs a new <see cref="AnimancerNode"/>.</summary> public AnimancerNode(AnimancerPlayable root) { if (root == null) { throw new ArgumentNullException("root"); } PortIndex = -1; Root = root; }
/************************************************************************************************************************/ /// <summary> /// Constructs a new <see cref="Vector2ControllerState"/> to play the 'controller' without connecting /// it to the <see cref="PlayableGraph"/>. You must call <see cref="AnimancerState.SetParent(AnimancerLayer)"/> /// or it won't actually do anything. /// </summary> private Vector2ControllerState(AnimancerPlayable root, RuntimeAnimatorController controller, Parameter parameterX, Parameter parameterY) : base(root, controller) { _ParameterX = parameterX; _ParameterX.Validate(Controller, AnimatorControllerParameterType.Float); _ParameterY = parameterY; _ParameterY.Validate(Controller, AnimatorControllerParameterType.Float); }
/************************************************************************************************************************/ /// <summary>Constructs a new <see cref="AnimancerNode"/>.</summary> protected AnimancerNode(AnimancerPlayable root) { if (root == null) { throw new ArgumentNullException("root"); } Index = -1; Root = root; }
/************************************************************************************************************************/ /// <summary> /// Constructs a new <see cref="Float2ControllerState"/> to play the `controller` without connecting /// it to the <see cref="PlayableGraph"/>. /// </summary> private Float2ControllerState(AnimancerPlayable root, RuntimeAnimatorController controller, Parameter parameterX, Parameter parameterY, bool resetStatesOnStop = true) : base(root, controller, resetStatesOnStop) { _ParameterX = parameterX; _ParameterX.ValidateHasParameter(Controller, AnimatorControllerParameterType.Float); _ParameterY = parameterY; _ParameterY.ValidateHasParameter(Controller, AnimatorControllerParameterType.Float); }
public static void AssertRoot(AnimancerNode node, AnimancerPlayable root) { #if UNITY_ASSERTIONS if (node.Root != root) { throw new ArgumentException($"{nameof(AnimancerNode)}.{nameof(AnimancerNode.Root)} mismatch:" + $" cannot use a node in an {nameof(AnimancerPlayable)} that is not its {nameof(AnimancerNode.Root)}: " + node.GetDescription()); } #endif }
/************************************************************************************************************************/ /// <summary> /// Invokes the <see cref="AnimancerEvent.Sequence.OnEnd"/> callback of the state that is playing the animation /// which triggered the event. Returns true if such a state exists (even if it doesn't have a callback). /// </summary> internal bool TryInvokeOnEndEvent(AnimationEvent animationEvent) { for (int i = States.Count - 1; i >= 0; i--) { if (AnimancerPlayable.TryInvokeOnEndEvent(animationEvent, States[i])) { return(true); } } return(false); }
public static void Root(AnimancerNode node, AnimancerPlayable root) { if (node == null) { throw new ArgumentNullException("node"); } if (node.Root != root) { throw new ArgumentException("AnimancerNode.Root mismatch:" + " you are attempting to use a node in an AnimancerPlayable that is not it's root: " + node); } }
/************************************************************************************************************************/ /// <summary> /// Invokes the <see cref="AnimancerEvent.Sequence.OnEnd"/> callback of the state that is playing the animation /// which triggered the event. Returns true if such a state exists (even if it doesn't have a callback). /// </summary> internal bool TryInvokeOnEndEvent(AnimationEvent animationEvent) { var count = States.Count; for (int i = 0; i < count; i++) { if (AnimancerPlayable.TryInvokeOnEndEvent(animationEvent, States[i])) { return(true); } } return(false); }
/************************************************************************************************************************/ /// <summary>Removes a registered <see cref="ExitEvent"/> and returns true if there was one.</summary> public static bool Unregister(AnimancerPlayable animancer) { for (int i = animancer.PostUpdatableCount - 1; i >= 0; i--) { if (animancer.GetPostUpdatable(i) is ExitEvent exit) { animancer.CancelPostUpdate(exit); exit.Release(); return(true); } } return(false); }
/************************************************************************************************************************/ /// <summary>Calls <see cref="AnimancerPlayable.Play(AnimationClip)"/> or <see cref="AnimancerPlayable.Play(ITransition)"/>.</summary> /// <remarks>Returns null if the `clipOrTransition` is null or an unsupported type.</remarks> public static AnimancerState TryPlay(AnimancerPlayable animancer, Object clipOrTransition) { if (clipOrTransition is AnimationClip clip) { return(animancer.Play(clip)); } else if (clipOrTransition is ITransitionDetailed transition) { return(animancer.Play(transition)); } else { return(null); } }
public static void Root(AnimancerNode node, AnimancerPlayable root) { #if UNITY_ASSERTIONS if (node == null) { throw new ArgumentNullException("node"); } if (node.Root != root) { throw new ArgumentException("AnimancerNode.Root mismatch:" + " cannot use a node in an AnimancerPlayable that is not its Root: " + node.GetDescription()); } #endif }
/************************************************************************************************************************/ #region Fields and Properties /************************************************************************************************************************/ /// <summary>[Internal] Creates a new <see cref="AnimancerLayer"/>.</summary> internal AnimancerLayer(AnimancerPlayable root, int index) { Root = root; Index = index; CreatePlayable(); if (ApplyParentAnimatorIK) { _ApplyAnimatorIK = root.ApplyAnimatorIK; } if (ApplyParentFootIK) { _ApplyFootIK = root.ApplyFootIK; } }
/// <summary> /// Called by Unity when this component becomes disabled or inactive. Acts according to the /// <see cref="ActionOnDisable"/>. /// </summary> protected virtual void OnDisable() { if (!IsPlayableInitialised) { return; } switch (_ActionOnDisable) { case DisableAction.Stop: Stop(); _Playable.PauseGraph(); break; case DisableAction.Pause: _Playable.PauseGraph(); break; case DisableAction.Continue: break; case DisableAction.Reset: Debug.Assert(_Animator.isActiveAndEnabled, "DisableAction.Reset failed because the Animator is not enabled." + " This most likely means you are disabling the GameObject and the Animator is above the" + " AnimancerComponent in the Inspector so it got disabled right before this method was called." + " See the Inspector of " + this + " to fix the issue or use DisableAction.Stop and call Animator.Rebind" + " manually before disabling the GameObject.", this); Stop(); _Animator.Rebind(); _Playable.PauseGraph(); break; case DisableAction.Destroy: _Playable.Destroy(); _Playable = null; break; default: throw new ArgumentOutOfRangeException("ActionOnDisable"); } }
/// <summary>[Pro-Only] /// Sets this list as the <see cref="Layers"/> and the <see cref="Playable"/> used to mix them. /// </summary> protected void Activate(AnimancerPlayable root, Playable mixer) { #if UNITY_ASSERTIONS if (Root != root) { throw new ArgumentException( $"{nameof(AnimancerPlayable)}.{nameof(LayerList)}.{nameof(Root)} mismatch:" + $" cannot use a list in an {nameof(AnimancerPlayable)} that is not its {nameof(Root)}"); } #endif _Layers = root.Layers._Layers; _Count = root.Layers._Count; root._RootPlayable.DisconnectInput(0); root.Graph.Connect(mixer, 0, root._RootPlayable, 0); root.Layers = this; root._LayerMixer = mixer; }
/************************************************************************************************************************/ #region Fields and Properties /************************************************************************************************************************/ /// <summary>[Internal] Creates a new <see cref="AnimancerLayer"/>.</summary> internal AnimancerLayer(AnimancerPlayable root, int index) { #if UNITY_ASSERTIONS GC.SuppressFinalize(this); #endif Root = root; Index = index; CreatePlayable(); if (ApplyParentAnimatorIK) { _ApplyAnimatorIK = root.ApplyAnimatorIK; } if (ApplyParentFootIK) { _ApplyFootIK = root.ApplyFootIK; } }
/************************************************************************************************************************/ /// <summary> /// Constructs a new <see cref="LinearMixerState"/> without connecting it to the <see cref="PlayableGraph"/>. /// You must call <see cref="AnimancerState.SetParent(AnimancerLayer)"/> or it won't actually do anything. /// </summary> private LinearMixerState(AnimancerPlayable root) : base(root) { }
/************************************************************************************************************************/ /// <summary> /// Constructs a new <see cref="DirectionalMixerState"/> without connecting it to the <see cref="PlayableGraph"/>. /// You must call <see cref="AnimancerState.SetParent(AnimancerLayer)"/> or it won't actually do anything. /// </summary> protected DirectionalMixerState(AnimancerPlayable root) : base(root) { }
/************************************************************************************************************************/ #region Fields and Properties /************************************************************************************************************************/ /// <summary>[Internal] Constructs a new <see cref="AnimancerLayer"/>.</summary> internal AnimancerLayer(AnimancerPlayable root, int index) { Root = root; Index = index; CreatePlayable(); }
/************************************************************************************************************************/ /// <summary>[Internal] Creates a new <see cref="StateDictionary"/>.</summary> internal StateDictionary(AnimancerPlayable root) => Root = root;
/************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #region Public Methods /************************************************************************************************************************/ /// <summary>Constructs a new <see cref="AnimancerState"/>.</summary> public AnimancerState(AnimancerPlayable root) : base(root) { }
/************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #region Initialisation /************************************************************************************************************************/ /// <summary> /// Constructs a new <see cref="LinearMixerState"/> without connecting it to the <see cref="PlayableGraph"/>. /// </summary> protected ManualMixerState(AnimancerPlayable root) : base(root) { }
/************************************************************************************************************************/ /// <summary>[Internal] Creates a new <see cref="LayerList"/>.</summary> internal LayerList(AnimancerPlayable root, out Playable layerMixer) { Root = root; layerMixer = LayerMixer = AnimationLayerMixerPlayable.Create(root._Graph, 1); Root._Graph.Connect(layerMixer, 0, Root._RootPlayable, 0); }
/************************************************************************************************************************/ /// <summary>Calls <see cref="ITransition.CreateState"/> and <see cref="ITransition.Apply"/>.</summary> public static AnimancerState CreateStateAndApply(this ITransition transition, AnimancerPlayable root = null) { var state = transition.CreateState(); state.SetRoot(root); transition.Apply(state); return(state); }