// 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 /************************************************************************************************************************/ } } }