Beispiel #1
0
        /// <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);
        }
Beispiel #2
0
        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());
            }
        }
Beispiel #4
0
 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.");
     }
 }
Beispiel #5
0
        /************************************************************************************************************************/

        private static void ForceFinishFade(AnimancerNode node)
        {
            var weight = node.TargetWeight;

            node.SetWeight(weight);
            if (weight == 0)
            {
                node.Stop();
            }
        }
Beispiel #6
0
        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
        }
Beispiel #7
0
        /************************************************************************************************************************/

        /// <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);
        }
Beispiel #8
0
        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);
            }
        }
Beispiel #9
0
        /************************************************************************************************************************/

        /// <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;
            }
        }
Beispiel #10
0
        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
        }
Beispiel #11
0
        /// <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);
            }
        }
Beispiel #12
0
        /************************************************************************************************************************/

        /// <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);
        }
Beispiel #13
0
        /// <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);
        }
Beispiel #14
0
        /// <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);
        }
Beispiel #15
0
        /************************************************************************************************************************/

        private void Release()
        {
            _Callback = null;
            _Node     = null;
            ObjectPool.Release(this);
        }
Beispiel #16
0
        /************************************************************************************************************************/

        /// <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;
Beispiel #17
0
 /// <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)
 {
 }
Beispiel #18
0
 /// <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)
 {
 }
Beispiel #20
0
 /// <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);
 }
Beispiel #21
0
 /// <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);
 }
Beispiel #22
0
 /// <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);
Beispiel #23
0
 /// <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)
 {
 }
Beispiel #28
0
 /// <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)
 {
 }
Beispiel #29
0
 public NodeWeight(AnimancerNode node)
 {
     Node           = node;
     StartingWeight = node.Weight;
 }
Beispiel #30
0
 /// <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;
 }