Skip to content

Input Buffer

Added in version 2.0, the Input Buffer allows deffering execution of Handlers to a later moment.

This is usually desirable on some combat systems for example, when players may hit certain keys while animations are playing and cannot be interrupted, but the game should still react to such inputs to promote responsive experiences.

Input Buffer Design

The buffer is implemented using the Command design pattern as a conceptual foundation: Inputs that cannot be handled right away are encapsulated in a Command structure so they can be executed later on, when the buffer is closed.

Also, the Input Buffer is Animation Based, meaning that it will be opened and closed during certain points in an animation, established through an Animation Notify State.

Even though the Input Manager Component is responsible for managing the buffer by default, the Input Buffer functionality is defined by the InputBufferInterface and any components that implement this interface can be engaged by the animation, as long as they are enabled. The Manager Component itself can be disabled as a buffer, if you want to replace it with your own Buffer Component. This makes the Input Buffer system quite flexible.

Buffer, not Queue

The default Input Buffer provided by the system is exactly that, a Buffer and not a Queue. Difference being that, only one command will be executed later, when the buffer is closed.

Using the Buffer

To buffer any actions, the following steps are required:

  1. Mark any Input Handlers so they can be buffered;
  2. Add the Notify State to an animation that supports buffering inputs.

Allowing Input Handlers to be added to the Input Buffer

To add an Input Handler to the Buffer, you simply need to enable its Can Be Buffered property, when registering the Input Handler to a UNinjaInputSetupDataAsset.

Marking Handlers as Bufferable

You are buffering Input Actions

Please note that, even though you are marking Input Handlers as "bufferable", conceptually it's the Input Action that gets buffered by the Input Manager Component!

This means that the Input Manager Component will collect all bufferable handlers that would be activated from an Input Action, while the buffer is active, and then invoke them later on, when the buffer is closed.

You can make Handlers bufferable by default

Even though this system was designed to allow the decision of buffering Handlers or not to be made on a design level, through the Setup Data Asset, you can still set the default value of that property to true in sub-classes/Blueprints.

Opening and Closing the Input Buffer from Animations

The most straightforward way to open and close the Input Buffer is by using the provided Animation Notify State, aptly named "Input Buffer".

Placing the Buffer AnimNotifyState

Press SHIFT to scrub through the animation

While positioning Animation Notifies or Notify States, press and hold the SHIFT key to smooth the scrolling and previewng the animation at the same time.

Once the Notify State is placed, the following will occur:

  • Once the state Begins, the Input Buffer will open and the Input Manager Component will stop routing Input Actions to Handlers. Instead, it will save any Handlers that would be triggered, along with all their current Input Action Values, based on the buffering policy currenty in-place.

  • Once the state Ends, the Input Buffer will close and any stored Input Handlers related to one saved Action will be invoked using the saved values from before.

Do not end the state at the last animation frame

Notifies and Notify States tend to fail being invoked when they are placed at the very last frame of an Animation, so it's recommended to place them a couple of frames before the end.

Opening and Closing the Buffer from Other Sources

Even though the method above is the primary method to interact with the Buffer, you can open and close the buffer in other ways, if you choose to do so. That can be done via the InputBufferInterface.

Interacting with the Buffer via Interface

// ----------
// .h
public:

    UFUNCTION(BlueprintCallable, Category = "Custom Buffer Setup")
    void SetBufferState(bool bOpen);

// ----------
// .cpp
#include "NinjaInputManagerComponent.h"
#include "Interfaces/InputBufferInterface.h"

void AMyCustomCharacter::SetBufferState(bool bOpen) 
{
    if (bOpen)
    {
        IInputBufferInterface::Execute_OpenInputBuffer(InputManagerComponent);
    }
    else
    {
        IInputBufferInterface::Execute_CloseInputBuffer(InputManagerComponent);
    }
}

Default Input Buffer Implementation

The UNinjaInputBufferComponent is the default implementation of the InputBufferInterface and it also provides an additional useful feature that can be modified as necessary: the Buffering Policy.

There are three possible values for it:

  • Last Command: All Input Actions will be processed until the buffer is closed. When that happens, the last Action that was processed is the one that will be executed. This is the default mode.

  • First Command: Once the first valid Input Action is saved, no other Actions will be kept and this first Action that was processed is the one that will be executed.

  • Disabled: If you'd rather have your own buffer implementation, the you can disable the default buffer, by using this value.

Supporting policies in custom buffers

If you are implementing your own buffer and would like to support the same policies, feel free to use the EInputBufferMode enumeration to access these values.

Creating new Input Buffers

You can create your own Input Buffer by creating an Actor Component that implements the InputBufferInterface. If you decide to do so, make sure to disable the Input Manager Component's Buffer and mark your component as enabled, through the adequate interface method.

Another option which makes this processer easier is to create your component as a subclass of the base NinjaInputBufferComponent, which not only implements the appropriate interface, but also provides some additional utility methods.

This approach also allows you to only implement functionality for the methods that are relevant for your scenario, usually being the BufferInputCommands, which is responsible for deciding if a command should be stored for later use or not.

The FBufferedInputCommand Struct

Commands stored in the buffer are represented by the FBufferedInputCommand struct.

The main Input Manager Component will create these instances when input handlers are routed to the buffer, instead of being processed immediately. The reason why the BufferInputCommands receives an array of commands is because the commands in that array were all triggered by the same Action and Event, so they actually represent one player input.