// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // using System; namespace Animancer { /// [Pro-Only] /// A callback to be triggered after an /// either starts or finishes fading out to 0 . /// /// /// /// The is only checked at the end of the animation update /// so if it's set multiple times in the same frame then the callback might not be triggered. /// /// Most Finite State Machine /// 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. /// /// Example: see the constructor. /// /// /// https://kybernetik.com.au/animancer/api/Animancer/ExitEvent /// public class ExitEvent : Updatable { /************************************************************************************************************************/ private Action _Callback; /// The method to invoke when this event is triggered. public Action Callback { get => _Callback; set { _Callback = value; if (_Callback != null) EnableIfActive(); else Disable(); } } /************************************************************************************************************************/ private AnimancerNode _Node; /// The target node which determines when to trigger this event. public AnimancerNode Node { get => _Node; set { _Node = value; if (_Node != null) EnableIfActive(); else Disable(); } } /************************************************************************************************************************/ /// /// Should the be invoked when the starts fading out? /// Otherwise, it will be invoked after the reaches 0. /// Default is false. /// public bool InvokeOnStartExiting { get; set; } /************************************************************************************************************************/ /// Creates a new . /// /// /// Example: /// private ExitEvent _OnStateExited; /// /// void ExitEventExample(AnimancerComponent animancer, AnimationClip clip) /// { /// var state = animancer.Play(clip); /// /// // One line initialization: /// (_OnClipExit ??= new(state, OnStateExited)).Enable(); /// /// // Or two lines: /// _OnClipExit ??= new(state, OnStateExited); /// _OnClipExit.Enable(); /// } /// /// private void OnStateExited() /// { /// Debug.Log(_OnClipExit.State + " Exited"); /// } /// /// public ExitEvent( AnimancerNode node, Action callback, bool invokeOnStartExiting = false) { _Node = node; _Callback = Callback; InvokeOnStartExiting = invokeOnStartExiting; } /************************************************************************************************************************/ /// Registers this event to start receiving updates. public void Enable() { if (_Callback != null) _Node?.Graph?.RequirePostUpdate(this); } /// /// Registers this event to start receiving updates if the /// is above 0 (i.e. it's not fading out). /// public void EnableIfActive() { if (_Callback != null && _Node != null && _Node.Graph != null && _Node.TargetWeight > 0) _Node.Graph.RequirePostUpdate(this); } /************************************************************************************************************************/ /// Cancels this event to stop receiving updates. public void Disable() { _Node?.Graph?.CancelPostUpdate(this); } /************************************************************************************************************************/ /// public override void Update() { if (!_Node.IsValid()) return; if (InvokeOnStartExiting) { if (_Node.TargetWeight != 0) return; } else { if (_Node.EffectiveWeight > 0) return; } _Callback(); Disable(); } /************************************************************************************************************************/ } }