137 lines
6.1 KiB
C#
137 lines
6.1 KiB
C#
// 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
|
|
{
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Events ready to be invoked by the next <see cref="Invoker.InvokeAllAndClear"/>.</summary>
|
|
/// <remarks>
|
|
/// 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.
|
|
/// </remarks>
|
|
private static readonly List<Invocation>
|
|
InvocationQueue = new();
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Gathers delegates in a static list to be invoked at a later time by any child class.</summary>
|
|
/// 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
|
|
{
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Ensures that an appropriate <see cref="Invoker"/> has been created.</summary>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static Invoker Initialize(bool fixedUpdate)
|
|
=> fixedUpdate
|
|
? InvokerFixed.Initialize()
|
|
: InvokerDynamic.Initialize();
|
|
|
|
/// <summary>Ensures that an appropriate <see cref="Invoker"/> has been created.</summary>
|
|
[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);
|
|
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>[Internal] Adds an event to the queue to be invoked by the next update.</summary>
|
|
[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);
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>
|
|
/// In case <see cref="InvokeAllAndClear"/> 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.
|
|
/// </summary>
|
|
private static int _CurrentInvocation;
|
|
|
|
/// <summary>Invokes all queued events and clears the queue.</summary>
|
|
public static void InvokeAllAndClear()
|
|
{
|
|
while (_CurrentInvocation < InvocationQueue.Count)
|
|
InvocationQueue[_CurrentInvocation++].Invoke();
|
|
|
|
InvocationQueue.Clear();
|
|
_CurrentInvocation = 0;
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Returns an enumerator for all invocations currently in the queue.</summary>
|
|
public static List<Invocation>.Enumerator EnumerateInvocationQueue()
|
|
=> InvocationQueue.GetEnumerator();
|
|
|
|
/************************************************************************************************************************/
|
|
#if UNITY_ASSERTIONS
|
|
/************************************************************************************************************************/
|
|
|
|
private static readonly List<Invoker>
|
|
Instances = new();
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>[Assert-Only] Registers this instance.</summary>
|
|
protected virtual void Awake()
|
|
=> Instances.Add(this);
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>[Assert-Only] Un-registers this instance.</summary>
|
|
protected virtual void OnDestroy()
|
|
=> Instances.Remove(this);
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>[Assert-Only] Is there any <see cref="Behaviour.enabled"/> instance?</summary>
|
|
private static bool HasEnabledInstance
|
|
{
|
|
get
|
|
{
|
|
for (int i = 0; i < Instances.Count; i++)
|
|
if (Instances[i].enabled)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
#endif
|
|
/************************************************************************************************************************/
|
|
}
|
|
}
|
|
}
|
|
|