UE5 Enhanced Input in Pure C++
Introduction
Most tutorials I’ve seen only cover Enhanced Input for blueprints, but in this post I’ll explain how to implement Enhanced Input in pure C++. We’ll create an input action and input mapping for WASD and Left Stick movement, then tie it all together in the player controller.
Enable Enhanced Input
In your Game.Build.cs file, add capability for Enhanced Input:
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });
Create classes
- Launch the editor. 
- Go to Tools > New C++ Class 
- Click - All Classes, select- InputActionas the parent, and click- Next.
- I named the class “IA_Move”. - I also put this in an “Input” folder in my project. Note that you may have to remove “Input/” from the include path in the .cpp file for the project to build correctly.
 
- Click - Create Class.
- Create another class with - InputMappingContextas the parent. I prefixed this one with “IMC_”.
Input action
All I did for the input action class was add a constructor to specify the ValueType:
.h
public:
    UIA_Move();
.cpp
#include "IA_Move.h"
UIA_Move::UIA_Move()
{
	ValueType = EInputActionValueType::Axis2D;
}
We’ll use Axis2D since it will work for all of our movement inputs.
Input mapping context
For the context, all we need are some private variables and another constructor.
.h
#pragma once
#include "CoreMinimal.h"
#include "InputMappingContext.h"
#include "IA_Move.h"
#include "IMC_Earth.generated.h"
UCLASS()
class EARTH_API UIMC_Earth : public UInputMappingContext
{
	GENERATED_BODY()
private:
	UIA_Move *Move;
	UInputModifierNegate *Negate;
	UInputModifierSwizzleAxis *Swizzle;
	FEnhancedActionKeyMapping W;
	FEnhancedActionKeyMapping A;
	FEnhancedActionKeyMapping S;
	FEnhancedActionKeyMapping D;
	FEnhancedActionKeyMapping GamepadLeft2d;
public:
	UIMC_Earth();
};
.cpp
#include "IMC_Earth.h"
#include "IA_Move.h"
UIMC_Earth::UIMC_Earth()
{
	Move = NewObject<UIA_Move>();
	Negate = NewObject<UInputModifierNegate>();
	Swizzle = NewObject<UInputModifierSwizzleAxis>();
	Swizzle->Order = EInputAxisSwizzle::YXZ;
	W = FEnhancedActionKeyMapping(Move, EKeys::W);
	W.Modifiers.Add(Swizzle);
	Mappings.Add(W);
	A = FEnhancedActionKeyMapping(Move, EKeys::A);
	A.Modifiers.Add(Negate);
	Mappings.Add(A);
	S = FEnhancedActionKeyMapping(Move, EKeys::S);
	S.Modifiers.Add(Negate);
	S.Modifiers.Add(Swizzle);
	Mappings.Add(S);
	D = FEnhancedActionKeyMapping(Move, EKeys::D);
	Mappings.Add(D);
	GamepadLeft2d = FEnhancedActionKeyMapping(Move, EKeys::Gamepad_Left2D);
	Mappings.Add(GamepadLeft2d);
}
To understand what Negate and Swizzle do, first take a look at the D key, which has no modifiers. When D is pressed, it will return a positive X-axis value of 1.0.
- Ahas only the- Negatemodifier, which will flip positive 1.0 to negative 1.0.
- Whas only the- Swizzlemodifier, which turns an X-axis value to a Y-axis value since we “swizzled” the axes from- XYZto- YXZ.
- Swill return a negative Y-axis value because it has both a- Negateand a- Swizzle.
Configure the player controller
You could also set this up in the Character or Pawn classes, but I like using the Controller for input.
.h
- In your controller .h, add the following properties and methods: - public: UPROPERTY(EditAnywhere, Category = "Input") TSoftObjectPtr<UInputMappingContext> InputMapping; protected: virtual void SetupInputComponent() override; private: virtual void MoveCallback(const FInputActionInstance& Instance);
.cpp
It’s optional, but you can add this #define at the top of your .cpp to conveniently print messages to the screen. We’ll use it to test if our callback works.
#define print(text) if (GEngine) GEngine->AddOnScreenDebugMessage(-1, 1.5, FColor::White, text)
- Add the needed headers: - #include <EnhancedInputComponent.h> #include <EnhancedInputSubsystems.h>
- In your controller’s constructor, initialize the InputMapping: - InputMapping = NewObject<UIMC_Earth>();
- Add your input setup. - void AEarthPlayerController::SetupInputComponent() { print("setup input component called"); Super::SetupInputComponent(); if (ULocalPlayer* LocalPlayer = GetLocalPlayer()) { print("we have a local player"); if (UEnhancedInputLocalPlayerSubsystem* InputSystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>()) { print("we have an input system"); if (!InputMapping.IsNull()) { print("we have an input mapping"); InputSystem->AddMappingContext(InputMapping.LoadSynchronous(), 0); UEnhancedInputComponent* Input = Cast<UEnhancedInputComponent>(InputComponent); FEnhancedActionKeyMapping mapping = InputMapping->GetMapping(0); Input->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &AEarthPlayerController::MoveCallback); } } } }
- Add the callback for your action. - void AEarthPlayerController::MoveCallback(const FInputActionInstance& Instance) { FVector2D Value = Instance.GetValue().Get<FVector2D>(); print(Value.ToString()); }
And that should do it. If you run the game, you should see the x/y values corresponding to your inputs printed to the screen.
A few more points
One important thing to note is I am getting the Action that is stored in the input mapping via InputMapping->GetMapping(0). If you simply create another Action of the same type and try to feed that into BindAction, it won’t work. It has to be the action stored in the mapping itself. We only had to get the first one because all our mappings reference the same action.
Also, in my case, I have a default game mode that specifies a default pawn and controller, and I have a Player Start actor sitting in my level. This means as soon as the game is launched, the controller will auto possess the pawn, and I will have a local player. Your case may be different, so just something to keep in mind.
We didn’t discuss triggers, but those are driven by UInputTrigger and added the same way as modifiers.
I hope this post helps you add the rest of your inputs and actions, and good luck!
Support the blog! Buy a t-shirt or a mug! Or just buy me a beverage🧋. Thanks!
Check out my other projects:
- Emerald Geography - Learn geography on a 3D globe.
- Word Rummage - Random word generator, word finder, and dictionary.
- Color Changer web extension for Firefox and Chrome - Change colors of websites to make them easier to read.