| 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;            }            /************************************************************************************************************************/        }    }}
 |