Skip to content

Player Input

The Player Input class is responsible for processing hardware input from the player and passing the signals to the Player Controller to then trigger events bound to move the pawn, fire a weapon, or perform any other action.

In the past, the UPlayerInput class was used to handle player input, which could trigger special events for the Input Actions and Input Axis defined in the Project Settings (Engine → Input). This approach, however, is now deprecated in favor of the Enhanced Input Plugin which implements UEnhancedPlayerInput. Enhanced Input offers a number of advantages over the legacy system, and implements features such as radial dead zones, contextual input, and prioritization, as well as the ability to remap the controls at runtime with ease.

Unreal Engine comes bundled with two more input plugins, which are also worth mentioning. The Raw Input Plugin can be used to add support for specific devices that aren't properly handled by Microsoft's XInput API. Flight sticks and steering wheels fall into this category. There is also the Force Feedback Plugin, which would allow you to add rumble to gamepads and controllers.

Enhanced Input Plugin

The plugin is enabled by default in the recent versions of Unreal Engine. Its associated classes should also be set as the default for Player Input and Input Component in the Project Settings (Engine → Input).

Enhanced Input set as the default in Project Settings.

Now, if you right-click in the Content Browser, you should see the following options under Input:

Input Assets in the Content Browser.

Let us now take a look at the core elements of Enhanced Input.

Input Actions

In Enhanced Input, every action that can be triggered by the player, such as moving in the world, looking around, firing a weapon, and jumping, should be represented via an Input Action, simple data assets with a handful of attributes.

Input Action Details Panel.

The most important attribute of an Input Action is its Value Type, which can be:

  • Digital: Represented as a Boolean value, and commonly used for actions that have an On/Off state. E.g., pressing a button to pick up item could be represented as a Digital action.
  • Axis1D: Represented as a Float value, and commonly used for actions that have a range of values. An example would be using the middle mouse button to zoom in and out.
  • Axis2D: Represented as a Vector2D value. Axis2D can be helpful when representing actions such as moving or looking around.
  • Axis3D: Represented as a Vector value. Axis3D is less common, but it can be used to hold more complex data, such as motion controller information.

After deciding how you'd like to represent an action, you may want to use Modifiers and Triggers to your Input Actions, to further control how it processes user input and how the action reacts.

Modifiers

Modifiers are preprocessors that alter the raw input values that the engine receives before sending them out. They can be useful for applying sensitivity settings, smoothing input over multiple frames, or changing how input behaves based on the state of the player.

You can define your own Modifiers by inheriting from the UInputModifier class, and overriding the ModifyRaw() RPC, in either Blueprints or C++. Below is an example of a Modifier used in the Lyra Game Sample.

/* Applies an inversion of axis values based on a setting in the Lyra Shared game settings. */

UCLASS(NotBlueprintable, MinimalAPI, meta = (DisplayName = "Lyra Aim Inversion Setting"))
class ULyraInputModifierAimInversion : public UInputModifier {
    GENERATED_BODY()

public:

    // Custom parameters will show up in the Input Action editor.
    UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category=Settings, Config)
    bool bInvertVerticalAxis = false;

protected:

    virtual FInputActionValue ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime) override {
        {
            ULyraLocalPlayer* LocalPlayer = LyraInputModifiersHelpers::GetLocalPlayer(PlayerInput);
            if (!LocalPlayer) {
                return CurrentValue;
            }
            ULyraSettingsShared* Settings = LocalPlayer->GetSharedSettings();
            FVector NewValue = CurrentValue.Get<FVector>();
            if (bInvertVerticalAxis) {
                NewValue.Y *= -1.0f;
            }
            if (Settings->GetInvertHorizontalAxis()) {
                NewValue.X *= -1.0f;
            }
            return NewValue;
        }
    }
};
Bundled

The Enhanced Input Plugin comes bundled with a variety of Modifiers to perform common preprocessing tasks. The full list is shown below.

Modifier Description
Deadzone Input values within the range LowerThreshold - UpperThreshold will be remapped from 0-1. Values outside this range will be clamped.
FOV Scaling Apply FOV dependent scaling to input values, per axis.
Negate Inverts input per axis.
Response Curve Apply a user-defined response curve to input values, per axis.
Scalar Scales input by specified factor.
Scale By Delta Time Multiplies the input by DeltaTime.
Smooth Smooths the input over multiple frame.
Swizzle Input Axis Values Useful to, for example, map 1D input signal for the W key to a Axis2D movement action.
To World Space Auto-converts axes within the Input Action value into world space allowing the result to be directly plugged into functions that take world space values.
E.g. for a 2D input axis up/down is mapped to world X (forward), whilst axis left/right is mapped to world Y (right).

Triggers

Triggers determine whether user input (after it has passed through the specified Modifiers), should activate the corresponding Input Action.

By default, any user activity on an input will trigger on every tick. However, Triggers allow you to analyze the input, enabling you to check for minimum actuation values and validating patterns like short taps, prolonged holds, or the typical "press" or "release" events.

To implement your own Triggers, you must inherit from the UInputTrigger class. You then need to override the UpdateState() RPC, which must return an ETriggerState value. The choices are:

  • None: Conditions have not been met, and the Input Action should not be activated.
  • Ongoing: Conditions are partially met, and the Trigger is ongoing but is not yet over the hill.
  • Triggered: Conditions have been fully met, and the Trigger has entered its final state.

Another overridable method to consider is GetTriggerType(), returning an ETriggerType. This flag can be Explicit, Implicit, or Blocker. The trigger type only comes into play when multiple Triggers are assigned to the same Input Action. In such cases, the Input Action is triggered if:

  • At least one Explicit-type Trigger fires.
  • Every single Implicit-type Trigger fires.
  • No Blocker-type Trigger fires.

The following example shows a Trigger that fires once input has remained actuated (e.g., the button has remained pressed) for HoldTimeThreshold seconds.

UInputTriggerHold.h
UCLASS(NotBlueprintable, MinimalAPI, meta = (DisplayName = "Hold"))
class UInputTriggerHold final : public UInputTriggerTimedBase {
    GENERATED_BODY()

    bool bTriggered = false;

protected:

    virtual ETriggerState UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) override;

public:

    virtual ETriggerEventsSupported GetSupportedTriggerEvents() const override { return ETriggerEventsSupported::Ongoing; }

    // How long does the input have to be held to cause trigger?
    UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings", meta = (ClampMin = "0"))
    float HoldTimeThreshold = 1.0f;

    // Should this trigger fire only once, or fire every frame once the hold time threshold is met?
    UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings")
    bool bIsOneShot = false;

    virtual FString GetDebugState() const override { return HeldDuration ? FString::Printf(TEXT("Hold:%.2f/%.2f"), HeldDuration, HoldTimeThreshold) : FString(); }
};
"
UInputTriggerHold.cpp
ETriggerState UInputTriggerHold::UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) {
    // Update HeldDuration and derive base state
    ETriggerState State = Super::UpdateState_Implementation(PlayerInput, ModifiedValue, DeltaTime);

    // Trigger when HeldDuration reaches the threshold
    bool bIsFirstTrigger = !bTriggered;
    bTriggered = HeldDuration >= HoldTimeThreshold;

    if (bTriggered) {
        return (bIsFirstTrigger || !bIsOneShot) ? ETriggerState::Triggered : ETriggerState::None;
    }
    return State;
} 
Bundled

The following are the Triggers that come with the Enhanced Input Plugin out of the box.

Trigger Description
Chorded Action Applies a Chord action that must be triggering for this trigger’s action to trigger.
E.g., in AC2, you are able to attack a guard by pressing the X, but this would only take place if you were already holding down right bumper to allow high profile actions. In Enhanced Input, you'll need to add this trigger to the attack action and set high profile as its Chord Action.
Combo All actions in the Combo Actions array must be triggered to trigger the action this trigger is on. Actions must also be completed in the order specified by the combo actions array. This will only trigger for one frame before resetting the combo trigger’s progress.
Think of a double jump action that is triggered by pressing the space bar twice, or the combos in Street Fighter 6.
Down Trigger fires when the input exceeds the Actuation Threshold.
When there are no triggers bound, this is the default behavior (Threshold=0).
Hold Trigger fires once input has remained actuated for Hold Time Threshold seconds.
Trigger may optionally fire once, or repeatedly fire.
Hold And Release Trigger fires when input is released after having been actuated for at least Hold Time Threshold seconds.
Pressed Trigger fires once only when input exceeds the actuation threshold. Holding the input will not cause further triggers.
Pulse Trigger that fires at an Interval, in seconds, while input is actuated.
Released Trigger returns Ongoing whilst input exceeds the actuation threshold, and then fires once only when input drops back below the actuation threshold.
Tap Input must be actuated and then released within Tap Release Time Threshold seconds to trigger.

Mapping Contexts

Mapping Contexts bind hardware events to Input Actions. For example, you can associate WASD keys and the Gamepad Left Thumbstick movements with an Input Action for moving. You can assign additional Modifiers and Triggers to the Input Actions, as well.

Mapping Contexts in the Project Settings.

Mapping Contexts can be added and removed at runtime. This makes it easier to manage a large number of Actions. You can change how certain inputs behave depending on the current state of the player.

As an example, you can give a character who can swim, walk, and drive vehicles multiple Input Mapping Contexts; one for common actions that are available and always mapped to the same user inputs, and another one for each individual mode of travel. Developers can then place the vehicle-related Input Actions into a separate Input Mapping Context, which is added to the local player when entering a vehicle, and removed from the local player when exiting the vehicle.

You can add a Mapping Context to your Pawn / Character to the player in either C++:

Class Member Definition
UPROPERTY(EditAnywhere, Category="Input")
TSoftObjectPtr<UInputMappingContext> InputMapping;
BeginPlay()
if (ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(Player)) {
    if (UEnhancedInputLocalPlayerSubsystem* InputSystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>()) {
        if (!InputMapping.IsNull()) {
            InputSystem->AddMappingContext(InputMapping.LoadSynchronous(), Priority);
        }
    }
}

or Blueprints:

Adding a Mapping Context in Blueprints.

You can use RemoveMappingContext() or ClearAllMappings() to remove contexts dynamically.

Input Listeners

After adding a Mapping Context to your Pawn / Character, you should be able to bind a callback function to each Input Action in C++:

// You should override SetupPlayerInputComponent() to set up input bindings.
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
    UEnhancedInputComponent* Input = Cast<UEnhancedInputComponent>(PlayerInputComponent);
    // You can bind to any of the trigger events here by changing the "ETriggerEvent" enum value
    Input->BindAction(AimingInputAction, ETriggerEvent::Triggered, this, &AFooBar::SomeCallbackFunc);
}

void AMyCharacter::SomeCallbackFunc(const FInputActionInstance& Instance) {
    // Get the value of the Input Action for whatever type you want here...
    FVector VectorValue = Instance.GetValue().Get<FVector>();
    FVector2D 2DAxisValue = Instance.GetValue().Get<FVector2D>();
    float FloatValue = Instance.GetValue().Get<float>(); 
    bool BoolValue = Instance.GetValue().Get<bool>();
    // Do your cool stuff here!
} 

or in Blueprints, by simply looking up the name of the Input Action:

Adding an Input Listener in Blueprints.

In either case, a triggered Input Action will have a Trigger State, which depends on the state and type of the assigned Triggers. The possible states are:

  • Triggered: The action was triggered. This means that it has completed the evaluation of all trigger requirements. For example, a "Press and Release" trigger is sent when the user releases the key.
  • Started: An event occurred that began trigger evaluation. For example, the first press of a "Double tap" trigger will call the "Started" state once.
  • Ongoing: The trigger is still being processed. For example, a "Press and hold" action is ongoing while the user is holding down the button before the specified duration is reached. Depending on the triggers, this event will fire every tick while the action is evaluated once it receives an input value.
  • Completed: The trigger evaluation process is completed.
  • Canceled: The triggering was canceled. For example, a user lets go of a button before a "Press and Hold" action can be triggered.

In C++, these states are represented by the ETriggerEvent enum, and are accessible via FInputActionInstance.GetTriggerEvent() in the callback function.

Platform Setting

You may want different input settings for different platforms, such as rotating the face buttons on the Nintendo Switch or altering the actions available on mobile devices. Enhanced Input provides a per-platform Mapping Context Redirect that allows you to do this easily. Any time the mapping context is referenced on a specific platform, it will be replaced by the value in the map when the mappings are rebuilt.

In order to define your Mapping Context Redirects, you need to create a Blueprint, with EnhancedInputPlatformData as the parent class, for every platform. You can then specify which Mapping Context should be mapped to another on the corresponding platform.

Details Panel for an Enhanced Input Platform Data Blueprint.

After creating the blueprints, you need to head to Project Settings → Engine → Enhanced Input → Platform Settings to then set the associated Redirect for each platform.

Enhanced Input Platform Settings.

Debugging

Enhanced Input offers a few commands to help you debug your input mappings.

By entering showdebug enhancedinput in the console, Unreal Engine will show you the state of every relevant Input Action and their associated hardware inputs, allowing to debug your bindings:

Debugging Enhanced Input.

You also have the option of debugging your connected input devices by using showdebug devices:

Debugging connected input devices.

Injecting Input

Enhanced Input also brings along the concept of Injecting Input to your player. This option allows you to simulate hardware input. You must use the Input.+key command to start simulating input and Input.-key to stop it.

Below is an example of setting the Gamepad_Left2D key:

Input.+key Gamepad_Left2D X=0.7 Y=0.5
Input.-key Gamepad_Left2D

You can find the names of hardware inputs in InputCoreTypes.cpp.

You also have the option of injecting input to the player in C++:

UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer());
UEnhancedPlayerInput* PlayerInput = Subsystem->GetPlayerInput();
FInputActionValue ActionValue(1.0f); // This can be a bool, float, FVector2D, or FVector
PlayerInput->InjectInputForAction(InputAction, ActionValue); 

and Blueprints:

Injecting Input in Blueprints.