KeyChange.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. using System;
  3. namespace Animancer.FSM
  4. {
  5. /// <summary>A static access point for the details of a key change in a <see cref="StateMachine{TKey, TState}"/>.</summary>
  6. /// <remarks>
  7. /// This system is thread-safe.
  8. /// <para></para>
  9. /// Documentation: <see href="https://kybernetik.com.au/animancer/docs/manual/fsm/changing-states">Changing States</see>
  10. /// </remarks>
  11. /// https://kybernetik.com.au/animancer/api/Animancer.FSM/KeyChange_1
  12. ///
  13. public struct KeyChange<TKey> : IDisposable
  14. {
  15. /************************************************************************************************************************/
  16. [ThreadStatic]
  17. private static KeyChange<TKey> _Current;
  18. private IKeyedStateMachine<TKey> _StateMachine;
  19. private TKey _PreviousKey;
  20. private TKey _NextKey;
  21. /************************************************************************************************************************/
  22. /// <summary>Is a <see cref="KeyChange{TKey}"/> of this type currently occurring?</summary>
  23. public static bool IsActive => _Current._StateMachine != null;
  24. /// <summary>The <see cref="KeyChange{TKey}"/> in which the current change is occurring.</summary>
  25. /// <remarks>This will be null if no change is currently occurring.</remarks>
  26. public static IKeyedStateMachine<TKey> StateMachine => _Current._StateMachine;
  27. /************************************************************************************************************************/
  28. /// <summary>The key being changed from.</summary>
  29. /// <exception cref="InvalidOperationException">[Assert-Only]
  30. /// <see cref="IsActive"/> is false so this property is likely being accessed on the wrong generic type.
  31. /// </exception>
  32. public static TKey PreviousKey
  33. {
  34. get
  35. {
  36. #if UNITY_ASSERTIONS
  37. if (!IsActive)
  38. throw new InvalidOperationException(StateExtensions.GetChangeError(typeof(TKey), typeof(StateMachine<,>), "Key"));
  39. #endif
  40. return _Current._PreviousKey;
  41. }
  42. }
  43. /************************************************************************************************************************/
  44. /// <summary>The key being changed into.</summary>
  45. /// <exception cref="InvalidOperationException">[Assert-Only]
  46. /// <see cref="IsActive"/> is false so this property is likely being accessed on the wrong generic type.
  47. /// </exception>
  48. public static TKey NextKey
  49. {
  50. get
  51. {
  52. #if UNITY_ASSERTIONS
  53. if (!IsActive)
  54. throw new InvalidOperationException(StateExtensions.GetChangeError(typeof(TKey), typeof(StateMachine<,>), "Key"));
  55. #endif
  56. return _Current._NextKey;
  57. }
  58. }
  59. /************************************************************************************************************************/
  60. /// <summary>[Internal]
  61. /// Assigns the parameters as the details of the currently active change and creates a new
  62. /// <see cref="KeyChange{TKey}"/> containing the details of the previously active change so that disposing
  63. /// it will re-assign those previous details to be current again in case of recursive state changes.
  64. /// </summary>
  65. /// <example><code>
  66. /// using (new KeyChange&lt;TState&gt;(previousKey, nextKey))
  67. /// {
  68. /// // Do the actual key change.
  69. /// }
  70. /// </code></example>
  71. internal KeyChange(IKeyedStateMachine<TKey> stateMachine, TKey previousKey, TKey nextKey)
  72. {
  73. this = _Current;
  74. _Current._StateMachine = stateMachine;
  75. _Current._PreviousKey = previousKey;
  76. _Current._NextKey = nextKey;
  77. }
  78. /************************************************************************************************************************/
  79. /// <summary>[<see cref="IDisposable"/>]
  80. /// Re-assigns the values of this change (which were the previous values from when it was created) to be the
  81. /// currently active change. See the constructor for recommended usage.
  82. /// </summary>
  83. /// <remarks>
  84. /// Usually this will be returning to default values (nulls), but if one state change causes another then the
  85. /// second one ending will return to the first which will then return to the defaults.
  86. /// </remarks>
  87. public void Dispose()
  88. {
  89. _Current = this;
  90. }
  91. /************************************************************************************************************************/
  92. /// <summary>Returns a string describing the contents of this <see cref="KeyChange{TKey}"/>.</summary>
  93. public override string ToString() => IsActive ?
  94. $"{nameof(KeyChange<TKey>)}<{typeof(TKey).FullName}" +
  95. $">({nameof(PreviousKey)}={PreviousKey}" +
  96. $", {nameof(NextKey)}={NextKey})" :
  97. $"{nameof(KeyChange<TKey>)}<{typeof(TKey).FullName}(Not Currently Active)";
  98. /// <summary>Returns a string describing the contents of the current <see cref="KeyChange{TKey}"/>.</summary>
  99. public static string CurrentToString() => _Current.ToString();
  100. /************************************************************************************************************************/
  101. }
  102. }