Skip to content
Moyor's Cauldron
Back to Journal

UE5 Enhanced Input: Quick Notes

August 15, 2026 #UE5#Input
Contents

Enhanced Input replaced the old input bindings. After a few years of use, here are the notes I should have written down much earlier.

The core idea of Enhanced Input is fully decoupling physical keys (Raw Input) from abstract intent (Input Action), and processing input logic through a pipelined data flow.

1. Modifiers stack — order matters

A Modifier is essentially a linear processing pipeline (pipelined vector processing): input data flows through each Modifier function top to bottom.

To avoid distorted diagonal movement or broken dead zones, follow this ordering rule:

  1. Swizzle YXZ: axis rearranging (e.g. mapping a 1D key input onto the Y axis of a Vector2D).
  2. Negate: sign flipping.
  3. Dead Zone: filtering mechanical jitter of physical sticks/keys.
  4. Scale / FOV Scaling: value scaling or sensitivity factors.

Key pitfalls:

  • Dead Zone vs Scale order:

    • Scale before Dead Zone (wrong): if the raw input is 0.15, Scale(2.0) turns it into 0.3. With a dead zone of 0.2, jitter that should have been filtered now crosses the threshold.
    • Dead Zone before Scale (correct): the dead zone only truncates the raw normalized input (0–1) linearly/radially; once it is clean, multiply by sensitivity or scaling.
  • Swizzle vs Negate order:

    • When mapping WASD to a 2D vector: S goes through Negate to flip the Y axis; A needs Swizzle YXZ first (moving the X-axis input onto the Y axis), then Negate. Swapping the order scrambles the directional mapping.

2. One action, many triggers (the Trigger lifecycle)

A single InputAction can host multiple Triggers (Pressed, Hold, Pulse…), dispatching callbacks through a state-machine model at runtime.

Trigger event lifecycle

[key down] ──> Started ──> Ongoing (held / charging) ──> Triggered (condition met) ──> Completed (released)
                            └── (released midway) ──> Canceled
Event (ETriggerEvent)When it firesTypical use
StartedKey just pressed / detection beginsPlay wind-up SFX, open charge UI
OngoingEvaluating (called every frame)Update charge progress, measure hold duration
TriggeredCondition fully met (per frame or once)Fire attack, continuous movement, full-charge release
CompletedKey released after a successful triggerThrow projectile, tap-release actions
CanceledInterrupted before conditions were metCharge interrupted / manually canceled

Design pattern

Instead of creating three Input Actions for “start charging / charging / release”, a single IA_ChargeAttack suffices — branch on GetTriggerEvent() inside the C++ / Blueprint callback:

void AMyCharacter::OnChargeAttack(const FInputActionInstance& ActionInstance)
{
    switch (ActionInstance.GetTriggerEvent())
    {
    case ETriggerEvent::Started:
        // 1. play charge SFX / open VFX
        break;
    case ETriggerEvent::Ongoing:
        // 2. update charge progress: ActionInstance.GetElapsedTime()
        break;
    case ETriggerEvent::Triggered:
        // 3. fully charged, release the heavy attack
        break;
    case ETriggerEvent::Canceled:
        // 4. interrupted midway, reset UI and state
        break;
    }
}

3. Dynamic IMC injection and priority

Dynamically stacking multiple contexts via UEnhancedInputLocalPlayerSubsystem is Enhanced Input’s killer feature over the legacy system.

Priority stacking rules

  • High priority overrides: when several IMCs are stacked, the higher-Priority context processes key mappings first.
  • Consume Input: with Consume Input enabled on an IMC or IA, once a high-priority context responds to a key (e.g. E in a UI menu), the event is not forwarded to lower-priority contexts (e.g. world interaction on E).

A typical priority architecture:

[Priority 10] IMC_UI / IMC_Menu (Consume Input on — blocks character control)
       ▲
[Priority 2]  IMC_Aiming / IMC_Vehicle (overrides specific actions, e.g. RMB aim / vehicle camera)
       ▲
[Priority 0]  IMC_Default (base movement, jumping, camera)

4. C++ binding pitfalls

  1. Module dependency: add "EnhancedInput" to your .Build.cs and include:
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
  1. Safe casting:
if (UEnhancedInputComponent* PEI = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
    PEI->BindAction(IA_Move, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
}
  1. Callback signature choice:
    • Only need the current axis/key value: void Move(const FInputActionValue& Value);
    • Need full metadata (TriggerEvent, elapsed time…): void Move(const FInputActionInstance& ActionInstance);

5. Going further: custom Modifiers & Triggers

  • Custom Modifier: inherit UInputModifier, override ModifyRaw_Implementation. Great for “scale Pitch sensitivity by camera pitch” or “non-linear smoothing while aiming down sights”.
  • Custom Trigger: inherit UInputTrigger, override UpdateState_Implementation. Great for double-tap, chord keys, and other game-specific trigger conditions.