// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace Animancer
{
/// https://kybernetik.com.au/animancer/api/Animancer/AnimancerEvent
partial struct AnimancerEvent
{
/************************************************************************************************************************/
/// Events ready to be invoked by the next .
///
/// This field should be inside the Invoker class.
/// But that can potentially cause a TypeLoadException if Invoker initializes before AnimancerEvent.
/// Having it out in AnimancerEvent avoids that possibility.
///
private static readonly List
InvocationQueue = new();
/************************************************************************************************************************/
/// Gathers delegates in a static list to be invoked at a later time by any child class.
/// https://kybernetik.com.au/animancer/api/Animancer/Invoker
[DefaultExecutionOrder(-30000)]// Run as soon as possible in whatever update cycle is being executed.
[ExecuteAlways]
public abstract class Invoker : MonoBehaviour
{
/************************************************************************************************************************/
/// Ensures that an appropriate has been created.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Invoker Initialize(bool fixedUpdate)
=> fixedUpdate
? InvokerFixed.Initialize()
: InvokerDynamic.Initialize();
/// Ensures that an appropriate has been created.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Invoker Initialize(AnimatorUpdateMode updateMode)
{
const AnimatorUpdateMode FixedUpdateMode =
#if UNITY_2023_1_OR_NEWER
AnimatorUpdateMode.Fixed;
#else
AnimatorUpdateMode.AnimatePhysics;
#endif
return Initialize(updateMode == FixedUpdateMode);
}
/************************************************************************************************************************/
/// [Internal] Adds an event to the queue to be invoked by the next update.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Add(Invocation invocation)
{
#if UNITY_ASSERTIONS
if (!HasEnabledInstance)
Debug.LogWarning(
$"There is no currently enabled {nameof(AnimancerEvent)}.{nameof(Invoker)}" +
$" so events will not be invoked.");
#endif
InvocationQueue.Add(invocation);
}
/************************************************************************************************************************/
///
/// In case gets called recursively,
/// we need to avoid invoking the same event multiple times
/// without the performance cost of immediately removing them each from the queue.
///
private static int _CurrentInvocation;
/// Invokes all queued events and clears the queue.
public static void InvokeAllAndClear()
{
while (_CurrentInvocation < InvocationQueue.Count)
InvocationQueue[_CurrentInvocation++].Invoke();
InvocationQueue.Clear();
_CurrentInvocation = 0;
}
/************************************************************************************************************************/
/// Returns an enumerator for all invocations currently in the queue.
public static List.Enumerator EnumerateInvocationQueue()
=> InvocationQueue.GetEnumerator();
/************************************************************************************************************************/
#if UNITY_ASSERTIONS
/************************************************************************************************************************/
private static readonly List
Instances = new();
/************************************************************************************************************************/
/// [Assert-Only] Registers this instance.
protected virtual void Awake()
=> Instances.Add(this);
/************************************************************************************************************************/
/// [Assert-Only] Un-registers this instance.
protected virtual void OnDestroy()
=> Instances.Remove(this);
/************************************************************************************************************************/
/// [Assert-Only] Is there any instance?
private static bool HasEnabledInstance
{
get
{
for (int i = 0; i < Instances.Count; i++)
if (Instances[i].enabled)
return true;
return false;
}
}
/************************************************************************************************************************/
#endif
/************************************************************************************************************************/
}
}
}