StateChange.cs 5.6 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 state change in a <see cref="StateMachine{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/StateChange_1
  12. ///
  13. public struct StateChange<TState> : IDisposable where TState : class, IState
  14. {
  15. /************************************************************************************************************************/
  16. [ThreadStatic]
  17. private static StateChange<TState> _Current;
  18. private StateMachine<TState> _StateMachine;
  19. private TState _PreviousState;
  20. private TState _NextState;
  21. /************************************************************************************************************************/
  22. /// <summary>Is a <see cref="StateChange{TState}"/> of this type currently occurring?</summary>
  23. public static bool IsActive => _Current._StateMachine != null;
  24. /// <summary>The <see cref="StateMachine{TState}"/> in which the current change is occurring.</summary>
  25. /// <remarks>This will be null if no change is currently occurring.</remarks>
  26. public static StateMachine<TState> StateMachine => _Current._StateMachine;
  27. /************************************************************************************************************************/
  28. /// <summary>The state currently 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 TState PreviousState
  33. {
  34. get
  35. {
  36. #if UNITY_ASSERTIONS
  37. if (!IsActive)
  38. throw new InvalidOperationException(StateExtensions.GetChangeError(typeof(TState), typeof(StateMachine<>)));
  39. #endif
  40. return _Current._PreviousState;
  41. }
  42. }
  43. /************************************************************************************************************************/
  44. /// <summary>The state 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 TState NextState
  49. {
  50. get
  51. {
  52. #if UNITY_ASSERTIONS
  53. if (!IsActive)
  54. throw new InvalidOperationException(StateExtensions.GetChangeError(typeof(TState), typeof(StateMachine<>)));
  55. #endif
  56. return _Current._NextState;
  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="StateChange{TState}"/> 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 StateChange&lt;TState&gt;(stateMachine, previousState, nextState))
  67. /// {
  68. /// // Do the actual state change.
  69. /// }
  70. /// </code></example>
  71. internal StateChange(StateMachine<TState> stateMachine, TState previousState, TState nextState)
  72. {
  73. this = _Current;
  74. _Current._StateMachine = stateMachine;
  75. _Current._PreviousState = previousState;
  76. _Current._NextState = nextState;
  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="StateChange{TState}"/>.</summary>
  93. public override string ToString() => IsActive ?
  94. $"{nameof(StateChange<TState>)}<{typeof(TState).FullName}" +
  95. $">({nameof(PreviousState)}='{_PreviousState}'" +
  96. $", {nameof(NextState)}='{_NextState}')" :
  97. $"{nameof(StateChange<TState>)}<{typeof(TState).FullName}(Not Currently Active)";
  98. /// <summary>Returns a string describing the contents of the current <see cref="StateChange{TState}"/>.</summary>
  99. public static string CurrentToString() => _Current.ToString();
  100. /************************************************************************************************************************/
  101. }
  102. }