// 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();
}
/************************************************************************************************************************/
}
}