123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- using System;
- using UnityEngine;
- namespace Animancer.FSM
- {
- public partial class StateMachine<TState>
- {
- /// <summary>
- /// A simple system that can <see cref="InputBuffer{TStateMachine}.Buffer"/> a state then try to enter it every
- /// time <see cref="InputBuffer{TStateMachine}.Update(float)"/> is called until the
- /// <see cref="InputBuffer{TStateMachine}.TimeOut"/> expires.
- /// </summary>
- ///
- /// <remarks>
- /// <strong>Documentation:</strong>
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/fsm/utilities#input-buffers">
- /// Input Buffers</see>
- /// <para></para>
- /// See <see cref="StateMachine{TState}.InputBuffer{TStateMachine}"/> for example usage.
- /// </remarks>
- ///
- /// https://kybernetik.com.au/animancer/api/Animancer.FSM/InputBuffer
- ///
- public class InputBuffer : InputBuffer<StateMachine<TState>>
- {
- /************************************************************************************************************************/
- /// <summary>Creates a new <see cref="InputBuffer"/>.</summary>
- public InputBuffer() { }
- /// <summary>Creates a new <see cref="InputBuffer"/> for the specified `stateMachine`.</summary>
- public InputBuffer(StateMachine<TState> stateMachine) : base(stateMachine) { }
- /************************************************************************************************************************/
- }
- /// <summary>
- /// A simple system that can <see cref="Buffer"/> a state then try to enter it every time
- /// <see cref="Update(float)"/> is called until the <see cref="TimeOut"/> expires.
- /// </summary>
- ///
- /// <remarks>
- /// <strong>Documentation:</strong>
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/fsm/utilities#input-buffers">
- /// Input Buffers</see>
- /// <para></para>
- /// <strong>Example:</strong><code>
- /// public StateMachine<CharacterState> stateMachine;// Initialized elsewhere.
- ///
- /// [SerializeField] private CharacterState _Attack;
- /// [SerializeField] private float _AttackInputTimeOut = 0.5f;
- ///
- /// private StateMachine<CharacterState>.InputBuffer _InputBuffer;
- ///
- /// private void Awake()
- /// {
- /// // Initialize the buffer.
- /// _InputBuffer = new StateMachine<CharacterState>.InputBuffer(stateMachine);
- /// }
- ///
- /// private void Update()
- /// {
- /// // When input is detected, buffer the desired state.
- /// if (Input.GetButtonDown("Fire1"))// Left Click by default.
- /// {
- /// _InputBuffer.Buffer(_Attack, _AttackInputTimeOut);
- /// }
- ///
- /// // At the end of the frame, Update the buffer so it tries to enter the buffered state.
- /// // After the time out, it will clear itself so Update does nothing until something else is buffered.
- /// _InputBuffer.Update();
- /// }
- /// </code></remarks>
- ///
- /// https://kybernetik.com.au/animancer/api/Animancer.FSM/InputBuffer_1
- ///
- public class InputBuffer<TStateMachine> where TStateMachine : StateMachine<TState>
- {
- /************************************************************************************************************************/
- private TStateMachine _StateMachine;
- private Action _ForceDefaultState;
- /// <summary>The <see cref="StateMachine{TState}"/> this buffer is feeding input to.</summary>
- public TStateMachine StateMachine
- {
- get => _StateMachine;
- set
- {
- if (_StateMachine is WithDefault withDefault)
- withDefault.ForceSetDefaultState = _ForceDefaultState;
- _StateMachine = value;
- TryRegisterForceSetDefaultState();
- Clear();
- }
- }
- private void TryRegisterForceSetDefaultState()
- {
- if (_StateMachine is WithDefault withDefault)
- {
- _ForceDefaultState = withDefault.ForceSetDefaultState;
- withDefault.ForceSetDefaultState = TryEnterStateOrForceDefault;
- }
- }
- /************************************************************************************************************************/
- /// <summary>The <typeparamref name="TState"/> this buffer is currently attempting to enter.</summary>
- public TState State { get; set; }
- /// <summary>The amount of time left before the <see cref="State"/> is cleared.</summary>
- public float TimeOut { get; set; }
- /************************************************************************************************************************/
- /// <summary>Is this buffer currently trying to enter a <see cref="State"/>?</summary>
- public bool IsActive => State != null;
- /************************************************************************************************************************/
- /// <summary>Creates a new <see cref="InputBuffer{TStateMachine}"/>.</summary>
- public InputBuffer() { }
- /// <summary>Creates a new <see cref="InputBuffer{TStateMachine}"/> for the specified `stateMachine`.</summary>
- public InputBuffer(TStateMachine stateMachine)
- {
- _StateMachine = stateMachine;
- TryRegisterForceSetDefaultState();
- }
- /************************************************************************************************************************/
- /// <summary>Sets the <see cref="State"/> and <see cref="TimeOut"/>.</summary>
- /// <remarks>Doesn't actually attempt to enter the state until <see cref="Update(float)"/> is called.</remarks>
- public void Buffer(TState state, float timeOut)
- {
- State = state;
- TimeOut = timeOut;
- }
- /************************************************************************************************************************/
- /// <summary>Attempts to enter the <see cref="State"/> and returns true if successful.</summary>
- protected virtual bool TryEnterState()
- => StateMachine.TryResetState(State);
- /************************************************************************************************************************/
- /// <summary>
- /// Calls <see cref="TryEnterState"/>. If it fails, then <see cref="WithDefault.ForceSetDefaultState"/>.
- /// </summary>
- public void TryEnterStateOrForceDefault()
- {
- if (IsActive &&
- TryEnterState())
- return;
- _ForceDefaultState();
- }
- /************************************************************************************************************************/
- /// <summary>Calls <see cref="Update(float)"/> using <see cref="Time.deltaTime"/>.</summary>
- /// <remarks>This method should be called at the end of a frame after any calls to <see cref="Buffer"/>.</remarks>
- public bool Update()
- => Update(Time.deltaTime);
- /// <summary>
- /// Attempts to enter the <see cref="State"/> if there is one and returns true if successful. Otherwise the
- /// <see cref="TimeOut"/> is decreased by `deltaTime` and <see cref="Clear"/> is called if it reaches 0.
- /// </summary>
- /// <remarks>This method should be called at the end of a frame after any calls to <see cref="Buffer"/>.</remarks>
- public bool Update(float deltaTime)
- {
- if (IsActive)
- {
- if (TryEnterState())
- {
- Clear();
- return true;
- }
- else
- {
- TimeOut -= deltaTime;
- if (TimeOut < 0)
- Clear();
- }
- }
- return false;
- }
- /************************************************************************************************************************/
- /// <summary>Clears this buffer so it stops trying to enter the <see cref="State"/>.</summary>
- public virtual void Clear()
- {
- State = null;
- TimeOut = default;
- }
- /************************************************************************************************************************/
- }
- }
- }
|