StateChange.cs 5.8 KB

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