/// <summary> /// Gathers the current details of the <see cref="AnimancerNode.Root"/> and register this /// <see cref="CustomFade"/> to be updated by it so that it can replace the regular fade behaviour. /// </summary> protected void Apply(AnimancerNode node) { #if UNITY_ASSERTIONS AnimancerUtilities.Assert(node != null, "Node is null."); AnimancerUtilities.Assert(node.IsValid, "Node is not valid."); AnimancerUtilities.Assert(node.FadeSpeed != 0, $"Node is not fading ({nameof(node.FadeSpeed)} is 0)."); var animancer = node.Root; AnimancerUtilities.Assert(animancer != null, $"{nameof(node)}.{nameof(node.Root)} is null."); if (OptionalWarning.CustomFadeBounds.IsEnabled()) { if (CalculateWeight(0) != 0) { OptionalWarning.CustomFadeBounds.Log("CalculateWeight(0) != 0.", animancer.Component); } if (CalculateWeight(1) != 1) { OptionalWarning.CustomFadeBounds.Log("CalculateWeight(1) != 1.", animancer.Component); } } #endif _Time = 0; _Target = new NodeWeight(node); _FadeSpeed = node.FadeSpeed; _Layer = node.Layer; _CommandCount = _Layer.CommandCount; node.FadeSpeed = 0; FadeOutNodes.Clear(); node.Root.RequirePreUpdate(this); }
public static void AssertPlayable(AnimancerNode node) { #if UNITY_ASSERTIONS if (node._Playable.IsValid()) { return; } var description = node.ToString(); if (node is AnimancerState state) { state.Destroy(); } if (node.Root == null) { throw new InvalidOperationException( $"{nameof(AnimancerNode)}.{nameof(AnimancerNode.Root)} hasn't been set so it's" + $" {nameof(Playable)} hasn't been created. It can be set by playing the state" + $" or calling {nameof(AnimancerState.SetRoot)} on it directly." + $" {nameof(AnimancerState.SetParent)} would also work if the parent has a {nameof(AnimancerNode.Root)}." + $"\n• State: {description}"); } else { throw new InvalidOperationException( $"{nameof(AnimancerNode)}.{nameof(IPlayableWrapper.Playable)} has not been created." + $" {nameof(AnimancerNode.CreatePlayable)} likely needs to be called on it before performing this operation." + $"\n• State: {description}"); } #endif }
/// <summary>Appends the hierarchy path of this state through its <see cref="Parent"/>s.</summary> private static void AppendPath(StringBuilder path, AnimancerNode parent) { var parentState = parent as AnimancerState; if (parentState != null && parentState._Parent != null) { AppendPath(path, parentState._Parent); } else { path.Append("Layers[") .Append(parent.Layer.PortIndex) .Append("].States"); return; } var state = parent as AnimancerState; if (state != null) { state.AppendPortAndType(path); } else { path.Append(" -> ") .Append(parent.GetType()); } }
public static void Parent(AnimancerState state, AnimancerNode parent) { if (state.Parent != parent) { throw new ArgumentException("AnimancerState.Parent mismatch:" + " you are attempting to use a state in an AnimancerLayer that is not it's parent."); } }
/************************************************************************************************************************/ private static void ForceFinishFade(AnimancerNode node) { var weight = node.TargetWeight; node.SetWeight(weight); if (weight == 0) { node.Stop(); } }
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> /// Registers the `callback` to be triggered when the <see cref="AnimancerNode.EffectiveWeight"/> becomes 0. /// </summary> /// <remarks> /// The <see cref="AnimancerNode.EffectiveWeight"/> is only checked at the end of the animation update so if it /// is set to 0 then back to a higher number before the next update, the callback will not be triggered. /// </remarks> public static void Register(AnimancerNode node, Action callback) { #if UNITY_ASSERTIONS AnimancerUtilities.Assert(node != null, "Node is null."); AnimancerUtilities.Assert(node.IsValid, "Node is not valid."); #endif var exit = ObjectPool.Acquire <ExitEvent>(); exit._Callback = callback; exit._Node = node; node.Root.RequirePostUpdate(exit); }
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>If the `node` is fading, this methods logs a warning (Assert-Only) and cancels the fade.</summary> private static void Validate(AnimancerNode node) { if (node != null && node.FadeSpeed != 0) { #if UNITY_ASSERTIONS Debug.LogWarning($"The following {node.GetType().Name} is fading even though " + $"{nameof(DontAllowFade)} is active: {node.GetDescription()}", node.Root.Component as Object); #endif node.Weight = node.TargetWeight; } }
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 }
/// <summary> /// Connects this state to the 'parent' mixer at the specified 'portIndex'. /// <para></para> /// See also <see cref="AnimancerLayer.AddChild(AnimancerState)"/> to connect a state to an available port on a /// layer. /// </summary> public void SetParent(AnimancerNode parent, int portIndex) { if (_Parent != null) { _Parent.OnRemoveChild(this); } PortIndex = portIndex; _Parent = parent; SetWeightDirty(); if (parent != null) { parent.OnAddChild(this); } }
/************************************************************************************************************************/ /// <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 TryInvokeOnEndEventRecursive(AnimancerNode node, AnimationEvent animationEvent) { var childCount = node.ChildCount; for (int i = 0; i < childCount; i++) { var child = node.GetChild(i); if (TryInvokeOnEndEvent(child, animationEvent) || TryInvokeOnEndEventRecursive(child, animationEvent)) { return(true); } } return(false); }
/// <summary> /// Removes a registered <see cref="ExitEvent"/> targeting the specified `node` and returns true if there was /// one. /// </summary> public static bool Unregister(AnimancerNode node) { var animancer = node.Root; for (int i = animancer.PostUpdatableCount - 1; i >= 0; i--) { if (animancer.GetPostUpdatable(i) is ExitEvent exit && exit._Node == node) { animancer.CancelPostUpdate(exit); exit.Release(); return(true); } } return(false); }
/// <summary>Connects this state to the `parent` mixer at the specified `index`.</summary> /// <remarks> /// Use <see cref="AnimancerLayer.AddChild(AnimancerState)"/> instead of this method to connect a state to an /// available port on a layer. /// </remarks> public void SetParent(AnimancerNode parent, int index) { if (_Parent != null) { _Parent.OnRemoveChild(this); _Parent = null; } if (parent == null) { Index = -1; return; } SetRoot(parent.Root); Index = index; _Parent = parent; parent.OnAddChild(this); CopyIKFlags(parent); }
/************************************************************************************************************************/ private void Release() { _Callback = null; _Node = null; ObjectPool.Release(this); }
/************************************************************************************************************************/ /// <summary>Returns true if the `node` is not null and <see cref="AnimancerNode.IsValid"/>.</summary> public static bool IsValid(this AnimancerNode node) => node != null && node.IsValid;
/// <summary> /// Constructs a new <see cref="LinearMixerState"/> and connects it to the `parent` at the specified /// `index`. /// </summary> public ManualMixerState(AnimancerNode parent, int index) : base(parent, index) { }
/// <summary>Modify the current fade to use the specified `curve` to calculate the weight.</summary> /// <example>See <see cref="CustomFade"/>.</example> /// <remarks>The `curve` should follow the <see cref="OptionalWarning.CustomFadeBounds"/> guideline.</remarks> public static void Apply(AnimancerNode node, AnimationCurve curve) => Curve.Acquire(curve).Apply(node);
/// <summary> /// Constructs a new <see cref="DirectionalMixerState"/> and connects it to the 'parent's /// <see cref="IAnimationMixer.Playable"/> at the specified 'portIndex'. /// </summary> public DirectionalMixerState(AnimancerNode parent, int portIndex) : base(parent, portIndex) { }
/// <summary> /// Constructs a new <see cref="Vector3ControllerState"/> to play the 'controller' and /// connects it to the 'parent's <see cref="IAnimationMixer.Playable"/> at the specified 'portIndex'. /// </summary> public Vector3ControllerState(AnimancerNode parent, int portIndex, RuntimeAnimatorController controller, Parameter parameterX, Parameter parameterY, Parameter parameterZ) : this(parent.Root, controller, parameterX, parameterY, parameterZ) { SetParent(parent, portIndex); }
/// <summary> /// Constructs a new <see cref="FloatControllerState"/> to play the 'controller' and connects /// it to the 'parent's <see cref="IAnimationMixer.Playable"/> at the specified 'portIndex'. /// </summary> public FloatControllerState(AnimancerNode parent, int portIndex, RuntimeAnimatorController controller, Parameter parameter) : this(parent.Root, controller, parameter) { SetParent(parent, portIndex); }
/// <summary>Modify the current fade to use the specified `function` to calculate the weight.</summary> /// <example>See <see cref="CustomFade"/>.</example> public static void Apply(AnimancerNode node, Easing.Function function) => Delegate.Acquire(function.GetDelegate()).Apply(node);
/// <summary>Modify the current fade to use the specified `calculateWeight` delegate.</summary> /// <example>See <see cref="CustomFade"/>.</example> /// <remarks>The `calculateWeight` should follow the <see cref="OptionalWarning.CustomFadeBounds"/> guideline.</remarks> public static void Apply(AnimancerNode node, Func <float, float> calculateWeight) => Delegate.Acquire(calculateWeight).Apply(node);
/// <summary> /// Constructs a new <see cref="ControllerState"/> to play the `animatorController` and /// connects it to the `parent` at the specified `index`. /// </summary> public ControllerState(AnimancerNode parent, int index, RuntimeAnimatorController animatorController, bool keepStateOnStop = false) : this(parent.Root, animatorController, keepStateOnStop) { SetParent(parent, index); }
/// <summary> /// Constructs a new <see cref="Float3ControllerState"/> to play the `controller` and /// connects it to the `parent` at the specified `index`. /// </summary> public Float3ControllerState(AnimancerNode parent, int index, RuntimeAnimatorController controller, Parameter parameterX, Parameter parameterY, Parameter parameterZ, bool resetStatesOnStop = true) : this(parent.Root, controller, parameterX, parameterY, parameterZ, resetStatesOnStop) { SetParent(parent, index); }
/// <summary> /// Constructs a new <see cref="LinearMixerState"/> and connects it to the `parent` at the specified /// `index`. /// </summary> public LinearMixerState(AnimancerNode parent, int index) : base(parent, index) { }
/// <summary> /// Constructs a new <see cref="CartesianMixerState"/> and connects it to the `parent` at the specified /// `index`. /// </summary> public CartesianMixerState(AnimancerNode parent, int index) : base(parent, index) { }
/// <summary> /// Constructs a new <see cref="MixerState{T}"/> and connects it to the 'parent's /// <see cref="IAnimationMixer.Playable"/> at the specified 'portIndex'. /// </summary> public MixerState(AnimancerNode parent, int portIndex) : base(parent, portIndex) { }
public NodeWeight(AnimancerNode node) { Node = node; StartingWeight = node.Weight; }
/// <summary>[Internal] /// Called by <see cref="AnimancerNode.OnAddChild(IList{AnimancerState}, AnimancerState)"/> if the specified /// port is already occupied so it can be cleared without triggering any other calls. /// </summary> internal void ClearParent() { Index = -1; _Parent = null; }