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:

  1. Construct a new StateMachine<TState>.InputBuffer by passing in the target state machine.
  2. Whenever you would normally call TrySetState(state) on the state machine, call Buffer(state, timeOut) on the input buffer instead. It will call TrySetState and if it fails, then it will store the state to try again later.
  3. 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:

  1. Construct a new StateMachine<TState>.StateSelector by passing in the target state machine.
  2. 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.
  3. After you have added all the potential states you want it to choose from, call TrySetState, TryResetState, or ForceSetState to have it try to enter each of the states using the corresponding StateMachine<TState> method.
  4. It attempts to enter each state you have added in order of priority (highest first).
  5. 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.