Skip to content

Creating Input Handlers

Preparing the Input Action and Mapping Context

First, let's create two Input Actions: IA_PrimaryAction with a Pressed trigger, andIA_SecondaryAction with Pressed and Released triggers.

Then, either create or reuse a preferred Input Mapping Context to add both actions to it, mapping them to any keys that you'd like. In this example, we're using the left and right mouse buttons and also the right gamepad shoulder and trigger.

IA_PrimaryAction

IA_SecondaryAction

IMC_Tutorial

Use the Ninja Input assets as a starting point

The Ninja Input plugin has a few assets in its Content Folder that you can use as a starting point, including Input Actions, Mapping Contexts and Input Setups!

Always choose the right triggers

Always use the correct triggers for your Input Actions! Leaving it blank wiil default to the Down trigger, which is not an ideal choice for actions that should trigger only once.

Creating an Input Handler Blueprint

For the Primary Action, we'll create a Blueprint Handler, which can be done from the Editor, under the Input category, as shown below.

Creating an Input Handler Blueprint

Open the blueprint and navigate to the Class Defaults pannel. Since we are creating this handler for the Input Action, we can assign it to it right away. We can do the same for the Trigger Events, since we are interested in watching the Triggered one.

This will tell the Input Manager Component that this Handler is compatible with this Action and Trigger.

Input Handler Defaults

As per our Input Action Setup, we are interested in reating to the pressed key, so we need to implement the function that is related to that event.

Override the Blueprint Function

And now we can implement our function and in this example, we'll play an Animation Montage in the Character. In here, notice a few things:

  1. The Function is running in a const context, meaning you cannot modify the Handler's state.
  2. You have access to the Owning Pawn/Character/Controller through the Manager Component provided as a parameter.
  3. You also have access to the Value and the Input Action that triggered this Handler Event.

Implement the Blueprint Function

Defining a friendly name in Blueprints

If you'd like to have a friendly Display Name for your Input Handler, which will make it easier to find it in the Input Handler List, then make sure to set that name in the Class Settings pannel, in the Blueprint Display Name field.

Creating an Input Handler in C++

For the Secondary Action, we'll create a Subclass of UNinjaInputHandler in C++, so make sure to extend from the it, when you are creating a new C++ class in the Editor or your IDE of choice.

Similar to the Blueprint version, we can define the Input Action and Triggers in the Constructor.

Getting the right asset reference

You can obtain the value for the FObjectFinder by right-clicking the Input Action in the Editor and then selecting Copy Reference.

Implement the constructor as follows:

#pragma once
#include "NinjaInputHandler.h"
#include "UInputHandler_SecondaryAction.generated.h"

UCLASS(meta = (DisplayName = "Sample: Secondary Action"))
class UInputHandler_SecondaryAction : public UNinjaInputHandler
{
    GENERATED_BODY()

public:

    UInputHandler_SecondaryAction();

}
#include "InputHandler_SecondaryAction.h"

#include "InputAction.h"
#include "Runtime/CoreUObject/Public/UObject/ConstructorHelpers.h"

UInputHandler_SecondaryAction::UInputHandler_SecondaryAction() 
{
    // Setting the default trigger event.
    TriggerEvents.Add(ETriggerEvent::Triggered);

    // Loading the input action using the asset reference.
    static ConstructorHelpers::FObjectFinder<UInputAction> InputActionRef(TEXT("/Script/EnhancedInput.InputAction'/Game/Input/IA_SecondaryAction.IA_SecondaryAction'"));
    if (InputActionRef.Succeeded())
    {
        const TObjectPtr<UInputAction> InputAction = InputActionRef.Object;
        InputActions.AddUnique(InputAction);
    }
}

As for the implementation of our functions, we are tracking the trigger events for Pressed and Released, which is something that could be used for a "Channeled Spell" or "Blocking", "Aiming" or "Crouching" for example.

class UInputHandler_SecondaryAction : public UNinjaInputHandler
{

    // ...

protected:

    virtual void HandleTriggeredEvent_Implementation(
        UNinjaInputManagerComponent* Manager, 
        const FInputActionValue& Value,
        const UInputAction* InputAction) const override;

}
#include "NinjaInputComponent.h"
#include "GameFramework/Character.h"

void UInputHandler_SecondaryAction::HandleTriggeredEvent_Implementation(
    UNinjaInputManagerComponent* Manager,
    const FInputActionValue& Value, 
    const UInputAction* InputAction) const
{
    ACharacter* OwningCharacter = Cast<ACharacter>(Manager->GetPawn());
    if (IsValid(OwningCharacter))
    {
        if (OwningCharacter->bIsCrouched)
        {
            OwningCharacter->UnCrouch();
        }
        else
        {
            OwningCharacter->Crouch();
        }
    }
}
No need to re-implement crouching

The code above is just an example, the main point is to demonstrate how to create handlers in both C++ and Blueprints, but they are not "game-ready" and you already have a Crouch Handler available!

Defining a friendly name

If you'd like to have a friendly Display Name for your Input Handler, which will make it easier to find it in the Input Handler List, in C++ make sure to set that name in the DisplayName value, in the UCLASS macro. In Blueprints, go to the Class Settings Pannel and set that name in the Blueprint Display Name property.

Configuring the Data Asset

The last step is to create a new Input Setup and assign it to the Character's Input Component. All these steps were already covered in the Initial Setup page. But this is how the Data Asset should look like, with the Mapping Context and Handlers.

Add Handlers to the Setup Asset

You created your first Input Handlers

This should give you an idea of how the process of creating Handlers works, both in Blueprints and C++. However, before creating your own Handlers, make sure to check the vast collection of Handlers already available and maybe they can already fulfill your requirements, or at least serve as a base Class!

Final Thoughts

Creating Input Handlers is quite straightforward!

As mentioned above, make sure to explore the provided library of Handlers to get familiarized with what's available, before deciding on create new Handlers from scratch!

Here are some important things to keep in mind, while creating your handlers.

Retrieving a World Reference

Since Input Handlers are usually created by a Data Object, they don't have a direct reference to the UWorld instance. This means that, if you need to access it, then do so via the Input Manager instance, which is always provided to the handler function.

Handlers Run in an Immutable Context

All functions that can be implemented in Handlers are executing in an immutable context, meaning the Handler object cannot be modified. This happens because the same Handler instance can be executed for multiple actions and multiple characters, even!

So if you need to persist state, do so in your owning Pawn/Controller. Furthermore, it's highly recommended to retrieve or save such values using an Interface as that removes the Cast requirements and memory allocation from doing so.

Use the Correct Input Triggers

This can't be stressed enough: Make sure to use the correct triggers for your Input Actions! If you don't set any triggers, they will automatically execute the Down trigger which is less than ideal for common scenarios such as one-off ability triggers, UI events and so on.

Here's a table with some suggestions of the appropriate setups for some common scenarios:

Scenario Implementation
Continuous Movement Down will trigger an ongoing event, while the button is down.
Common Actions Pressed will trigger once, when the key is pressed.
Press for x seconds to Interact Hold, if configured to trigger once, that will happen after pressing a button for x seconds.
Interaction start/cancel In addition to the above, create another Action for the same button that tracks the Pressed and Released events.
Momentary Crouch, Spring, Aim, Block, etc. A combination of Pressed and Released which will trigger on both events. If you want to "toggle instead, then just Pressed is enough.
Shift or Ctrl + Some Key Chorded Action, where the modifier keys (Shift, Ctrl, etc) are registered as their own Input Actions, mapped and added to this trigger.

Add or Remove Contexts as necessary

If your character has many input contexts such as default movement, riding a horse, inventory UI and so on, consider having separated Contexts for each one of those scenarios, with their proper dedicated Handlers, and then add/remove these contexts as necessary, using the proper methods shown in the Input Manager Component page.

Enable Logging Messages

While creating new Handlers or new Setups, consider enabling the global debug, in the Ninja Input Settings page. This is a quick way to make sure that all handlers are being correctly triggered, even when they are not performing the expected action.

Also, keep in mind that if you enable the Log Level to Verbose, a lot of information will be added to your console, which can immensely help with debugging issues. You can do that in the DefaultEngine.ini file, by adding the following lines:

[Core.Log]
LogNinjaInputHandler=Verbose
LogNinjaManagerComponent=Verbose