// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2023 Kybernetik // using System; namespace Animancer.FSM { /// A static access point for the details of a key change in a . /// /// This system is thread-safe. /// /// Documentation: Changing States /// /// https://kybernetik.com.au/animancer/api/Animancer.FSM/KeyChange_1 /// public struct KeyChange : IDisposable { /************************************************************************************************************************/ [ThreadStatic] private static KeyChange _Current; private IKeyedStateMachine _StateMachine; private TKey _PreviousKey; private TKey _NextKey; /************************************************************************************************************************/ /// Is a of this type currently occurring? public static bool IsActive => _Current._StateMachine != null; /// The in which the current change is occurring. /// This will be null if no change is currently occurring. public static IKeyedStateMachine StateMachine => _Current._StateMachine; /************************************************************************************************************************/ /// The key being changed from. /// [Assert-Only] /// is false so this property is likely being accessed on the wrong generic type. /// public static TKey PreviousKey { get { #if UNITY_ASSERTIONS if (!IsActive) throw new InvalidOperationException(StateExtensions.GetChangeError(typeof(TKey), typeof(StateMachine<,>), "Key")); #endif return _Current._PreviousKey; } } /************************************************************************************************************************/ /// The key being changed into. /// [Assert-Only] /// is false so this property is likely being accessed on the wrong generic type. /// public static TKey NextKey { get { #if UNITY_ASSERTIONS if (!IsActive) throw new InvalidOperationException(StateExtensions.GetChangeError(typeof(TKey), typeof(StateMachine<,>), "Key")); #endif return _Current._NextKey; } } /************************************************************************************************************************/ /// [Internal] /// Assigns the parameters as the details of the currently active change and creates a new /// containing the details of the previously active change so that disposing /// it will re-assign those previous details to be current again in case of recursive state changes. /// /// /// using (new KeyChange<TState>(previousKey, nextKey)) /// { /// // Do the actual key change. /// } /// internal KeyChange(IKeyedStateMachine stateMachine, TKey previousKey, TKey nextKey) { this = _Current; _Current._StateMachine = stateMachine; _Current._PreviousKey = previousKey; _Current._NextKey = nextKey; } /************************************************************************************************************************/ /// [] /// Re-assigns the values of this change (which were the previous values from when it was created) to be the /// currently active change. See the constructor for recommended usage. /// /// /// Usually this will be returning to default values (nulls), but if one state change causes another then the /// second one ending will return to the first which will then return to the defaults. /// public void Dispose() { _Current = this; } /************************************************************************************************************************/ /// Returns a string describing the contents of this . public override string ToString() => IsActive ? $"{nameof(KeyChange)}<{typeof(TKey).FullName}" + $">({nameof(PreviousKey)}={PreviousKey}" + $", {nameof(NextKey)}={NextKey})" : $"{nameof(KeyChange)}<{typeof(TKey).FullName}(Not Currently Active)"; /// Returns a string describing the contents of the current . public static string CurrentToString() => _Current.ToString(); /************************************************************************************************************************/ } }