StateMachine1.InputBuffer.cs 7.6 KB

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