Input Buffers
Rather than simply attempting a state change and giving up if it isn't allowed, it is often desirable to keep trying for a short time. For example, if the player is attacking and they press the attack button again, you probably want the current animation to finish first and then start the next attack in a combo as long as the input didn't occur too long ago. Note that "input" in this case does not only mean "button presses from the player", it can be used for any sort of events that might try to control a state machine (such as AI).
The StateMachine<TState>.InputBuffer
class allows you to easily implement this sort of buffering:
- Construct a
new StateMachine<TState>.InputBuffer
by passing in the target state machine. - Whenever you would normally call
TrySetState(state)
on the state machine, callBuffer(state, timeOut)
on the input buffer instead. It will callTrySetState
and if it fails, then it will store thestate
to try again later. - Call
Update
on the buffer every frame to have it try to enter the buffered state again (if there is one).
The Weapons example demonstrates how to use this system.
This system only buffers a single state at a time. If the player presses the attack button while they are already attacking then presses the block button before the first attack ends, it will only execute the block rather than performing the entire second attack before blocking. This assumes the latest command is the only one that matters, which is often good for things like player input, but might not be best for all situations. If you want to ensure that all commands are executed in order, you could make a similar class (perhaps called InputSequenceBuffer
) which manages a list of states instead of only a single BufferedState
.
State Selectors
The StateMachine<TState>.StateSelector
class provides an easy way to manage a prioritised list of potential states:
- Construct a
new StateMachine<TState>.StateSelector
by passing in the target state machine. - Add states to the selector by calling its
Add
method, which requires you to give them each a priority value. If multiple states have the same priority, the most recently added will be treated as a higher priority. - After you have added all the potential states you want it to choose from, call
TrySetState
,TryResetState
, orForceSetState
to have it try to enter each of the states using the correspondingStateMachine<TState>
method. - It attempts to enter each state you have added in order of priority (highest first).
- If it successfully enters a state, the selector is cleared.
If you want to define the priority of a state as part of the state class itself (rather than as a second parameter every time you call the Add
method), you can implement the IPrioritizable
interface. The IPrioritizable.Priority
is usually implemented as an abstract
or virtual
property in your base state class so that each state can override
it with their own priority.