2025-05-09 15:40:34 +08:00

167 lines
5.7 KiB
C#

// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
using System;
namespace Animancer
{
/// <summary>[Pro-Only]
/// A callback to be triggered after an <see cref="AnimancerNode"/>
/// either starts or finishes fading out to 0 <see cref="AnimancerNode.EffectiveWeight"/>.
/// </summary>
///
/// <remarks>
/// The <see cref="AnimancerNode.EffectiveWeight"/> 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.
/// <para></para>
/// 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.
/// <para></para>
/// <strong>Example:</strong> see the <see cref="ExitEvent(AnimancerNode, Action, bool)"/> constructor.
/// </remarks>
///
/// https://kybernetik.com.au/animancer/api/Animancer/ExitEvent
///
public class ExitEvent : Updatable
{
/************************************************************************************************************************/
private Action _Callback;
/// <summary>The method to invoke when this event is triggered.</summary>
public Action Callback
{
get => _Callback;
set
{
_Callback = value;
if (_Callback != null)
EnableIfActive();
else
Disable();
}
}
/************************************************************************************************************************/
private AnimancerNode _Node;
/// <summary>The target node which determines when to trigger this event.</summary>
public AnimancerNode Node
{
get => _Node;
set
{
_Node = value;
if (_Node != null)
EnableIfActive();
else
Disable();
}
}
/************************************************************************************************************************/
/// <summary>
/// Should the <see cref="Callback"/> be invoked when the <see cref="Node"/> starts fading out?
/// Otherwise, it will be invoked after the <see cref="AnimancerNode.EffectiveWeight"/> reaches 0.
/// Default is <c>false</c>.
/// </summary>
public bool InvokeOnStartExiting { get; set; }
/************************************************************************************************************************/
/// <summary>Creates a new <see cref="ExitEvent"/>.</summary>
///
/// <remarks>
/// <strong>Example:</strong><code>
/// 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");
/// }
/// </code></remarks>
///
public ExitEvent(
AnimancerNode node,
Action callback,
bool invokeOnStartExiting = false)
{
_Node = node;
_Callback = Callback;
InvokeOnStartExiting = invokeOnStartExiting;
}
/************************************************************************************************************************/
/// <summary>Registers this event to start receiving updates.</summary>
public void Enable()
{
if (_Callback != null)
_Node?.Graph?.RequirePostUpdate(this);
}
/// <summary>
/// Registers this event to start receiving updates if the
/// <see cref="AnimancerNode.TargetWeight"/> is above 0 (i.e. it's not fading out).
/// </summary>
public void EnableIfActive()
{
if (_Callback != null &&
_Node != null &&
_Node.Graph != null &&
_Node.TargetWeight > 0)
_Node.Graph.RequirePostUpdate(this);
}
/************************************************************************************************************************/
/// <summary>Cancels this event to stop receiving updates.</summary>
public void Disable()
{
_Node?.Graph?.CancelPostUpdate(this);
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void Update()
{
if (!_Node.IsValid())
return;
if (InvokeOnStartExiting)
{
if (_Node.TargetWeight != 0)
return;
}
else
{
if (_Node.EffectiveWeight > 0)
return;
}
_Callback();
Disable();
}
/************************************************************************************************************************/
}
}