Spring Sale: 30% off bundles with SPRINGBUNDLE or 15% off individual products with SPRING15 — ends Apr 15

StraySparkStraySpark
ProductsFree AssetsDocsBlogGamesAbout
StraySparkStraySpark

Game Studio & UE5 Tool Developers. Building professional-grade tools for the Unreal Engine community.

Products

  • Complete Toolkit (Bundle)
  • Procedural Placement Tool
  • Cinematic Spline Tool
  • Blueprint Template Library
  • DetailForge
  • UltraWire
  • Unreal MCP Server
  • Blender MCP Server
  • Godot MCP Server

Resources

  • Free Assets
  • Documentation
  • Blog
  • Changelog
  • Roadmap
  • FAQ
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 StraySpark. All rights reserved.

Back to Blog
tutorial
StraySparkMarch 26, 20265 min read
UE5 Enhanced Input System: The Complete Migration and Setup Guide 
Unreal EngineInputTutorialBlueprintsCpp

Why Enhanced Input?

The legacy input system in UE5 (axis mappings, action mappings in Project Settings) works but doesn't scale. Enhanced Input provides:

  • Context-based bindings: Different controls for on-foot, in-vehicle, swimming, in-menu
  • Input modifiers: Dead zones, sensitivity curves, inversion — all configurable per-action
  • Complex triggers: Hold, tap, double-tap, chord (multiple buttons) — built-in
  • Runtime remapping: Players can rebind controls at runtime
  • Multiple devices: Gamepad, keyboard, mouse, touch — handled cleanly

The legacy system is deprecated. New projects should use Enhanced Input exclusively, and existing projects should migrate before the legacy system is removed.

Core Concepts

Input Actions (IA)

An Input Action represents a gameplay action — not a button. "Jump," "Move," "Look," "Fire" are Input Actions. They're decoupled from physical inputs.

IA_Move → FInputActionValue (Axis2D: X/Y movement vector)
IA_Jump → FInputActionValue (Bool: pressed or not)
IA_Look → FInputActionValue (Axis2D: camera delta)
IA_Fire → FInputActionValue (Bool: pressed or not)
IA_Zoom → FInputActionValue (Axis1D: zoom amount)

Create Input Actions as assets: right-click Content Browser → Input → Input Action.

Value Type is critical:

  • Bool: On/off (Jump, Fire, Interact)
  • Axis1D: Single float (Zoom, Throttle)
  • Axis2D: Two floats (Move direction, Look direction)
  • Axis3D: Three floats (VR hand position)

Input Mapping Contexts (IMC)

Mapping Contexts bind physical inputs to Input Actions. You can have multiple contexts active simultaneously with priority ordering.

IMC_OnFoot (Priority: 1)
├── IA_Move → WASD / Left Stick
├── IA_Jump → Space / Face Button Bottom
├── IA_Look → Mouse / Right Stick
├── IA_Fire → Left Mouse / Right Trigger
└── IA_Interact → E / Face Button Left

IMC_Vehicle (Priority: 2)
├── IA_Steer → A/D / Left Stick X
├── IA_Accelerate → W / Right Trigger
├── IA_Brake → S / Left Trigger
└── IA_ExitVehicle → E / Face Button Left

IMC_Menu (Priority: 3)
├── IA_Navigate → WASD / Left Stick / D-Pad
├── IA_Confirm → Enter / Face Button Bottom
├── IA_Cancel → Escape / Face Button Right
└── IA_TabSwitch → Q/E / Bumpers

When the player enters a vehicle, add IMC_Vehicle and remove IMC_OnFoot. Menu opens? Add IMC_Menu with highest priority.

Modifiers

Modifiers transform raw input before it reaches your gameplay code:

  • Dead Zone: Ignore small stick movements (essential for gamepads)
  • Scalar: Multiply input value (sensitivity)
  • Negate: Invert axis (invert Y look)
  • Swizzle Input Axis Values: Remap axes (swap X and Y)
  • Smooth: Interpolate input over frames
  • Response Curve: Non-linear sensitivity curves (acceleration)

Apply modifiers per-binding in the Mapping Context. Different devices can have different modifiers for the same action.

Triggers

Triggers define when the action fires:

  • Down: Fires while button is held
  • Pressed: Fires once when button is first pressed
  • Released: Fires once when button is released
  • Hold: Fires after holding for a threshold time
  • Hold and Release: Fires when released after holding
  • Tap: Fires on quick press (under threshold time)
  • Pulse: Fires repeatedly at an interval while held
  • Combo: Fires when a sequence of actions is performed
  • Chorded Action: Fires only when another action is also active (e.g., Shift+Click)

Setting Up Enhanced Input

Step 1: Create Input Actions

For a basic character controller, create these Input Action assets:

Asset NameValue TypeDescription
IA_MoveAxis2DWASD / stick movement
IA_LookAxis2DMouse / right stick camera
IA_JumpBoolJump button
IA_SprintBoolSprint modifier
IA_InteractBoolInteraction button
IA_FireBoolPrimary attack

Step 2: Create a Mapping Context

Create an Input Mapping Context asset (IMC_Default) and bind:

IA_Move:

  • W → Y=1.0 (add Swizzle modifier: YXZ, then Negate modifier on Y)
  • S → Y=-1.0 (add Swizzle modifier: YXZ)
  • A → X=-1.0 (add Negate modifier)
  • D → X=1.0
  • Gamepad Left Stick → Axis2D (add Dead Zone modifier: 0.2)

IA_Look:

  • Mouse XY 2D-Axis → Axis2D
  • Gamepad Right Stick → Axis2D (add Dead Zone: 0.15, Scalar: 2.5)

IA_Jump:

  • Space Bar → Pressed trigger
  • Gamepad Face Button Bottom → Pressed trigger

Step 3: Bind in C++

// In your character header
void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;

void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
void StartJump(const FInputActionValue& Value);
void StopJump(const FInputActionValue& Value);

UPROPERTY(EditDefaultsOnly, Category = "Input")
TObjectPtr<UInputMappingContext> DefaultMappingContext;

UPROPERTY(EditDefaultsOnly, Category = "Input")
TObjectPtr<UInputAction> MoveAction;

UPROPERTY(EditDefaultsOnly, Category = "Input")
TObjectPtr<UInputAction> LookAction;

UPROPERTY(EditDefaultsOnly, Category = "Input")
TObjectPtr<UInputAction> JumpAction;
// In your character source
void AMyCharacter::BeginPlay()
{
    Super::BeginPlay();

    if (APlayerController* PC = Cast<APlayerController>(Controller))
    {
        if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
            ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
        {
            Subsystem->AddMappingContext(DefaultMappingContext, 0);
        }
    }
}

void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    UEnhancedInputComponent* EnhancedInput = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);

    EnhancedInput->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
    EnhancedInput->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyCharacter::Look);
    EnhancedInput->BindAction(JumpAction, ETriggerEvent::Started, this, &AMyCharacter::StartJump);
    EnhancedInput->BindAction(JumpAction, ETriggerEvent::Completed, this, &AMyCharacter::StopJump);
}

void AMyCharacter::Move(const FInputActionValue& Value)
{
    FVector2D MovementVector = Value.Get<FVector2D>();

    const FRotator Rotation = Controller->GetControlRotation();
    const FRotator YawRotation(0, Rotation.Yaw, 0);
    const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
    const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

    AddMovementInput(ForwardDirection, MovementVector.Y);
    AddMovementInput(RightDirection, MovementVector.X);
}

void AMyCharacter::Look(const FInputActionValue& Value)
{
    FVector2D LookVector = Value.Get<FVector2D>();
    AddControllerYawInput(LookVector.X);
    AddControllerPitchInput(LookVector.Y);
}

Step 4: Blueprint Alternative

In your Character Blueprint:

  1. Add Enhanced Input Component (usually automatic)
  2. In BeginPlay, get the Enhanced Input Local Player Subsystem and call Add Mapping Context
  3. In SetupPlayerInputComponent, use Enhanced Input Action bindings

The Blueprint nodes mirror the C++ API exactly.

Switching Contexts at Runtime

The power of Enhanced Input is context switching:

void AMyCharacter::EnterVehicle(AVehicle* Vehicle)
{
    if (UEnhancedInputLocalPlayerSubsystem* Subsystem = GetInputSubsystem())
    {
        Subsystem->RemoveMappingContext(OnFootMappingContext);
        Subsystem->AddMappingContext(VehicleMappingContext, 0);
    }
}

void AMyCharacter::ExitVehicle()
{
    if (UEnhancedInputLocalPlayerSubsystem* Subsystem = GetInputSubsystem())
    {
        Subsystem->RemoveMappingContext(VehicleMappingContext);
        Subsystem->AddMappingContext(OnFootMappingContext, 0);
    }
}

For menus, add a high-priority context rather than removing the gameplay context — this way the transition back is seamless.

Runtime Rebinding

Allow players to remap controls at runtime:

// Create a player-specific mapping context from a template
UInputMappingContext* PlayerContext = DuplicateObject(DefaultMappingContext, this);

// Modify a specific binding
FEnhancedActionKeyMapping& JumpMapping = PlayerContext->MapKey(JumpAction, EKeys::SpaceBar);
// Change to player's preferred key
PlayerContext->UnmapKey(JumpAction, EKeys::SpaceBar);
PlayerContext->MapKey(JumpAction, PlayerPreferredJumpKey);

// Apply the modified context
Subsystem->RemoveMappingContext(DefaultMappingContext);
Subsystem->AddMappingContext(PlayerContext, 0);

Save player bindings to a config file and restore them on game launch.

Migration from Legacy Input

Step-by-Step Migration

  1. Inventory your existing bindings: List every Action Mapping and Axis Mapping in Project Settings
  2. Create Input Actions: One IA per unique gameplay action
  3. Create Mapping Contexts: Group bindings by game state (on-foot, vehicle, menu)
  4. Update code: Replace InputComponent->BindAction/BindAxis with Enhanced Input bindings
  5. Test each binding: Verify all inputs work with both keyboard and gamepad
  6. Remove legacy bindings: Delete old mappings from Project Settings

Common Migration Pitfalls

Forgetting to add the Mapping Context: Enhanced Input actions won't fire without an active context. This is the #1 migration bug.

Wrong trigger event: Legacy BindAction uses IE_Pressed/IE_Released. Enhanced Input uses ETriggerEvent::Started/Completed. They're conceptually similar but not identical — Started fires on the trigger threshold, which may differ from a simple button press depending on your triggers.

Missing dead zones on gamepad: Legacy input had implicit dead zones. Enhanced Input requires explicit Dead Zone modifiers on stick bindings. Without them, your camera will drift.

Axis scaling differences: Legacy axis mappings had a Scale property. In Enhanced Input, use Scalar and Negate modifiers instead.

Enhanced Input is a significant upgrade over the legacy system. The initial setup is more work, but the flexibility for runtime remapping, context switching, and complex input handling pays for itself throughout development.

Tags

Unreal EngineInputTutorialBlueprintsCpp

Continue Reading

tutorial

Getting Started with UE5 PCG Framework: Build Your First Procedural World

Read more
tutorial

Nanite Foliage in UE5: The Complete Guide to High-Performance Vegetation

Read more
tutorial

UE5 Lumen Optimization Guide: Achieving 60fps with Dynamic Global Illumination

Read more
All posts