StateMachine1.InputBuffer.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. using System;
  3. using UnityEngine;
  4. namespace Animancer.FSM
  5. {
  6. public partial class StateMachine<TState>
  7. {
  8. /// <summary>
  9. /// A simple system that can <see cref="InputBuffer{TStateMachine}.Buffer"/> a state then try to enter it every
  10. /// time <see cref="InputBuffer{TStateMachine}.Update(float)"/> is called until the
  11. /// <see cref="InputBuffer{TStateMachine}.TimeOut"/> expires.
  12. /// </summary>
  13. ///
  14. /// <remarks>
  15. /// <strong>Documentation:</strong>
  16. /// <see href="https://kybernetik.com.au/animancer/docs/manual/fsm/utilities#input-buffers">
  17. /// Input Buffers</see>
  18. /// <para></para>
  19. /// See <see cref="StateMachine{TState}.InputBuffer{TStateMachine}"/> for example usage.
  20. /// </remarks>
  21. ///
  22. /// https://kybernetik.com.au/animancer/api/Animancer.FSM/InputBuffer
  23. ///
  24. public class InputBuffer : InputBuffer<StateMachine<TState>>
  25. {
  26. /************************************************************************************************************************/
  27. /// <summary>Creates a new <see cref="InputBuffer"/>.</summary>
  28. public InputBuffer() { }
  29. /// <summary>Creates a new <see cref="InputBuffer"/> for the specified `stateMachine`.</summary>
  30. public InputBuffer(StateMachine<TState> stateMachine) : base(stateMachine) { }
  31. /************************************************************************************************************************/
  32. }
  33. /// <summary>
  34. /// A simple system that can <see cref="Buffer"/> a state then try to enter it every time
  35. /// <see cref="Update(float)"/> is called until the <see cref="TimeOut"/> expires.
  36. /// </summary>
  37. ///
  38. /// <remarks>
  39. /// <strong>Documentation:</strong>
  40. /// <see href="https://kybernetik.com.au/animancer/docs/manual/fsm/utilities#input-buffers">
  41. /// Input Buffers</see>
  42. /// <para></para>
  43. /// <strong>Example:</strong><code>
  44. /// public StateMachine&lt;CharacterState&gt; stateMachine;// Initialized elsewhere.
  45. ///
  46. /// [SerializeField] private CharacterState _Attack;
  47. /// [SerializeField] private float _AttackInputTimeOut = 0.5f;
  48. ///
  49. /// private StateMachine&lt;CharacterState&gt;.InputBuffer _InputBuffer;
  50. ///
  51. /// private void Awake()
  52. /// {
  53. /// // Initialize the buffer.
  54. /// _InputBuffer = new StateMachine&lt;CharacterState&gt;.InputBuffer(stateMachine);
  55. /// }
  56. ///
  57. /// private void Update()
  58. /// {
  59. /// // When input is detected, buffer the desired state.
  60. /// if (Input.GetButtonDown("Fire1"))// Left Click by default.
  61. /// {
  62. /// _InputBuffer.Buffer(_Attack, _AttackInputTimeOut);
  63. /// }
  64. ///
  65. /// // At the end of the frame, Update the buffer so it tries to enter the buffered state.
  66. /// // After the time out, it will clear itself so Update does nothing until something else is buffered.
  67. /// _InputBuffer.Update();
  68. /// }
  69. /// </code></remarks>
  70. ///
  71. /// https://kybernetik.com.au/animancer/api/Animancer.FSM/InputBuffer_1
  72. ///
  73. public class InputBuffer<TStateMachine> where TStateMachine : StateMachine<TState>
  74. {
  75. /************************************************************************************************************************/
  76. private TStateMachine _StateMachine;
  77. private Action _ForceDefaultState;
  78. /// <summary>The <see cref="StateMachine{TState}"/> this buffer is feeding input to.</summary>
  79. public TStateMachine StateMachine
  80. {
  81. get => _StateMachine;
  82. set
  83. {
  84. if (_StateMachine is WithDefault withDefault)
  85. withDefault.ForceSetDefaultState = _ForceDefaultState;
  86. _StateMachine = value;
  87. TryRegisterForceSetDefaultState();
  88. Clear();
  89. }
  90. }
  91. private void TryRegisterForceSetDefaultState()
  92. {
  93. if (_StateMachine is WithDefault withDefault)
  94. {
  95. _ForceDefaultState = withDefault.ForceSetDefaultState;
  96. withDefault.ForceSetDefaultState = TryEnterStateOrForceDefault;
  97. }
  98. }
  99. /************************************************************************************************************************/
  100. /// <summary>The <typeparamref name="TState"/> this buffer is currently attempting to enter.</summary>
  101. public TState State { get; set; }
  102. /// <summary>The amount of time left before the <see cref="State"/> is cleared.</summary>
  103. public float TimeOut { get; set; }
  104. /************************************************************************************************************************/
  105. /// <summary>Is this buffer currently trying to enter a <see cref="State"/>?</summary>
  106. public bool IsActive => State != null;
  107. /************************************************************************************************************************/
  108. /// <summary>Creates a new <see cref="InputBuffer{TStateMachine}"/>.</summary>
  109. public InputBuffer() { }
  110. /// <summary>Creates a new <see cref="InputBuffer{TStateMachine}"/> for the specified `stateMachine`.</summary>
  111. public InputBuffer(TStateMachine stateMachine)
  112. {
  113. _StateMachine = stateMachine;
  114. TryRegisterForceSetDefaultState();
  115. }
  116. /************************************************************************************************************************/
  117. /// <summary>Sets the <see cref="State"/> and <see cref="TimeOut"/>.</summary>
  118. /// <remarks>Doesn't actually attempt to enter the state until <see cref="Update(float)"/> is called.</remarks>
  119. public void Buffer(TState state, float timeOut)
  120. {
  121. State = state;
  122. TimeOut = timeOut;
  123. }
  124. /************************************************************************************************************************/
  125. /// <summary>Attempts to enter the <see cref="State"/> and returns true if successful.</summary>
  126. protected virtual bool TryEnterState()
  127. => StateMachine.TryResetState(State);
  128. /************************************************************************************************************************/
  129. /// <summary>
  130. /// Calls <see cref="TryEnterState"/>. If it fails, then <see cref="WithDefault.ForceSetDefaultState"/>.
  131. /// </summary>
  132. public void TryEnterStateOrForceDefault()
  133. {
  134. if (IsActive &&
  135. TryEnterState())
  136. return;
  137. _ForceDefaultState();
  138. }
  139. /************************************************************************************************************************/
  140. /// <summary>Calls <see cref="Update(float)"/> using <see cref="Time.deltaTime"/>.</summary>
  141. /// <remarks>This method should be called at the end of a frame after any calls to <see cref="Buffer"/>.</remarks>
  142. public bool Update()
  143. => Update(Time.deltaTime);
  144. /// <summary>
  145. /// Attempts to enter the <see cref="State"/> if there is one and returns true if successful. Otherwise the
  146. /// <see cref="TimeOut"/> is decreased by `deltaTime` and <see cref="Clear"/> is called if it reaches 0.
  147. /// </summary>
  148. /// <remarks>This method should be called at the end of a frame after any calls to <see cref="Buffer"/>.</remarks>
  149. public bool Update(float deltaTime)
  150. {
  151. if (IsActive)
  152. {
  153. if (TryEnterState())
  154. {
  155. Clear();
  156. return true;
  157. }
  158. else
  159. {
  160. TimeOut -= deltaTime;
  161. if (TimeOut < 0)
  162. Clear();
  163. }
  164. }
  165. return false;
  166. }
  167. /************************************************************************************************************************/
  168. /// <summary>Clears this buffer so it stops trying to enter the <see cref="State"/>.</summary>
  169. public virtual void Clear()
  170. {
  171. State = null;
  172. TimeOut = default;
  173. }
  174. /************************************************************************************************************************/
  175. }
  176. }
  177. }