123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
- using System;
- namespace Animancer
- {
- /// <summary>[Pro-Only] A callback for when an <see cref="AnimancerNode.EffectiveWeight"/> becomes 0.</summary>
- ///
- /// <remarks>
- /// Most <see href="https://kybernetik.com.au/animancer/docs/manual/fsm">Finite State Machine</see> systems
- /// already have their own mechanism for notifying your code when a state is exited so this system is generally
- /// only useful when something like that is not already available.
- /// </remarks>
- ///
- /// <example><code>
- /// [SerializeField] private AnimancerComponent _Animancer;
- /// [SerializeField] private AnimationClip _Clip;
- ///
- /// private void Awake()
- /// {
- /// // Play the animation.
- /// var state = _Animancer.Play(_Clip);
- ///
- /// // Then give its state an exit event.
- /// ExitEvent.Register(state, () => Debug.Log("State Exited"));
- ///
- /// // That event will never actually get triggered because we are never playing anything else.
- /// }
- /// </code>
- /// Unlike Animancer Events, an <see cref="ExitEvent"/> will not be cleared automatically when you play something
- /// (because that's the whole point) so if you are playing the same animation repeatedly you will need to check its
- /// <see cref="AnimancerNode.EffectiveWeight"/> before registering the event (otherwise all the callbacks you register will
- /// stay active).
- /// <para></para><code>
- /// private void Update()
- /// {
- /// // Only register the exit event if the state was at 0 weight before.
- /// var state = _Animancer.GetOrCreate(_Clip);
- /// if (state.EffectiveWeight == 0)
- /// ExitEvent.Register(state, () => Debug.Log("State Exited"));
- ///
- /// // Then play the state normally.
- /// _Animancer.Play(state);
- /// // _Animancer.Play(_Clip); would work too, but we already have the state so using it directly is faster.
- /// }
- /// </code></example>
- ///
- /// https://kybernetik.com.au/animancer/api/Animancer/ExitEvent
- ///
- public class ExitEvent : Key, IUpdatable
- {
- /************************************************************************************************************************/
- private Action _Callback;
- private AnimancerNode _Node;
- /************************************************************************************************************************/
- /// <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);
- }
- /************************************************************************************************************************/
- /// <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>
- /// 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;
- }
- /************************************************************************************************************************/
- void IUpdatable.Update()
- {
- if (_Node.IsValid() && _Node.EffectiveWeight > 0)
- return;
- _Callback();
- _Node.Root.CancelPostUpdate(this);
- Release();
- }
- /************************************************************************************************************************/
- private void Release()
- {
- _Callback = null;
- _Node = null;
- ObjectPool.Release(this);
- }
- /************************************************************************************************************************/
- }
- }
|