KeyChange.cs 5.6 KB

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