Enhanced Input is the new input system provided as a plugin with Unreal Engine 5. And with the upcoming release of 5.1, as this post is written, the old input system has been flagged for deprecation. Thus, this post provides a basic foundation of how to use Enhanced Input within a C++ project for Unreal Engine 5.
Prerequisite
For clarity in this tutorial, I will be using the prefix InputExample
for most of the classes. Please name them as fit for your project. And I will be using the Third Person example project for demonstration.
As for the engine version, OS, and IDE used for this tutorial, I am using UnrealEngine 5.0.3 on Pop OS! (an Ubuntu-based Linux distribution), and code editing in Visual Studio Code. This tutorial should work on any UE5+ version, OS (like Windows and Mac), and IDE (like Visual Studio, etc).
And with that out of the way, let’s get started.
Setup
First, enable the Enhanced Input plugin, if it hasn’t been set already, and restart the editor.
For those that prefer to add the plugin manually in the IDE, add these lines of code to the Plugins section of your *.uproject file.
*.uproject
"Plugins": [
{
"Name": "EnhancedInput",
"Enabled": true
},
]
While we’re still in the IDE, open *.Build.cs file and add "EnhancedInput"
to the PublicDependencyModuleNames.
*.Build.cs
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"InputCore",
"EnhancedInput"
});
With the editor open, go to Project Settings->Input
. Then scroll down to Default Classes, change the Default Player Input Class from PlayerInput to EnhancedPlayerInput, and Default Input Component Class from InputComponent to EnhancedInputComponent.
And with that, the editor can be closed for now.
Input Mapping Context
Time to start writing some C/C++ code. Open the IDE of your choice and create a new character class that inherent from ACharacter
. For this example, I’ll name my AInputExampleCharacter
.
I like to categorize each of the systems into little #pragma region
for ease of readability, but this is totally optional and up to the programmer’s taste. Also, all of these functions and variables will be protected, so no other classes but its children can access them.
First, we need a pointer to the UInputMappingContext that will be created in the editor later. So start with these lines of code.
InputExampleCharacter.h
#pragma region /** Input */
protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Enhanced Input")
class UInputMappingContext* InputMapping;
#pragma endregion
Next, open or create the InputExampleCharacter.cpp
file (if not exist already) and scroll down to SetupPlayerInputComponent function.
Now that we have the pointer to the input mapping context, it needs to be added to the engine’s Subsystem for the LocalPlayer to use. Remove all the codes inside the SetupPlayerInputComponent function, and replace them with these new codes. Also, add these #include
so the compiler can find the necessary headers.
InputExampleCharacter.cpp
#include "InputMappingContext.h"
#include "EnhancedInputSubsystems.h"
void AInputExampleCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
// Get the player controller
APlayerController* PC = Cast<APlayerController>(GetController());
// Get the local player subsystem
UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer());
// Clear out existing mapping, and add our mapping
Subsystem->ClearAllMappings();
Subsystem->AddMappingContext(InputMapping, 0);
}
Compile the code, for now, to ensure there are no unexpected errors that pop up.
InputAction
With the mapping context added, it is time to move on to InputAction. This is similar to the ActionBinding found in the old input system. But instead of creating them in the Project Settings, the new system has the Action created as an object in the project folder, that can be modified at runtime.
When working in the blueprint, the InputAction can be easily accessed as an Input Event. But in C++, we don’t have that convenience. Thus, we must create pointers for these actions and assign them in the editor later.
Depending on the project, you can create an UInputAction* variable, and it would suffice. But I like to have a config file so I could add as many actions as I want in the future, and it would be more manageable.
To start, create a UDataAsset subclass. And I will name my, MyInputConfigData
.
MyInputConfigData.h
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "InputAction.h"
#include "MyInputConfigData.generated.h"
/**
* Store pointer to Input Action for native binding
*/
UCLASS()
class INPUTEXAMPLE_API UMyInputConfigData : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
UInputAction* InputMove;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
UInputAction* InputLook;
};
Don’t forget to add the #include
for the InputAction header. The INPUTEXAMPLE_API must be replaced with “YOURPROJECTNAME”_API. And I created two example pointers to Move and Look actions. Add as many as you need right here. Compile the code to catch any unexpected errors.
Next, add a pointer to the config file that we just created to the InputExampleCharacter.h
file. The object will be created in the editor later.
InputExampleCharacter.h
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Enhanced Input")
class UMyInputConfigData* InputActions;
And with that, these actions can be bound with functions. To do that, open the InputExampleCharacter.cpp
file and get back to SetupPlayerInputComponent. Add these lines of code to bind the Move and Look functions.
InputExampleCharacter.cpp
#include "EnhancedInput/Public/EnhancedInputComponent.h"
#include "MyInputConfigData.h"
// Get the EnhancedInputComponent
UEnhancedInputComponent* PEI = Cast<UEnhancedInputComponent>(PlayerInputComponent);
// Bind the actions
PEI->BindAction(InputActions->InputMove, ETriggerEvent::Triggered, this, &AInputExampleCharacter::Move);
PEI->BindAction(InputActions->InputLook, ETriggerEvent::Triggered, this, &AInputExampleCharacter::Look);
Replace AInputExampleCharacter with your character class name.
Now that we have them bound, time to create the Move and Look. These will just be a modified function of existing code in the example project.
InputExampleCharacter.h
#include "InputActionValue.h"
// Handle move input
void Move(const FInputActionValue& Value);
// Handle look input
void Look(const FInputActionValue& Value);
InputExampleCharacter.cpp
#include "InputActionValue.h"
void AInputExampleCharacter::Move(const FInputActionValue& Value)
{
if (Controller != nullptr)
{
const FVector2D MoveValue = Value.Get<FVector2D>();
const FRotator MovementRotation(0, Controller->GetControlRotation().Yaw, 0);
// Forward/Backward direction
if (MoveValue.Y != 0.f)
{
// Get forward vector
const FVector Direction = MovementRotation.RotateVector(FVector::ForwardVector);
AddMovementInput(Direction, MoveValue.Y);
}
// Right/Left direction
if (MoveValue.X != 0.f)
{
// Get right vector
const FVector Direction = MovementRotation.RotateVector(FVector::RightVector);
AddMovementInput(Direction, MoveValue.X);
}
}
}
void AInputExampleCharacter::Look(const FInputActionValue& Value)
{
if (Controller != nullptr)
{
const FVector2D LookValue = Value.Get<FVector2D>();
if (LookValue.X != 0.f)
{
AddControllerYawInput(LookValue.X);
}
if (LookValue.Y != 0.f)
{
AddControllerPitchInput(LookValue.Y);
}
}
}
Compile the code, and it is time to hook everything up in the editor.
Finalizing
Open the project in the Editor, and time to create the MappingContex and InputAction objects.
Create 2 InputActions with the Value Type of Axis2D (Vector2D) by Right-click in the Content Browser -> Input.
Next is the Mapping Context. Create the object and map the actions with appropriate keybinds. Here is my set and which modifiers to use for WASD.
Also, don’t forget about our InputConfig data asset by Right-click in the Content Browser -> Miscellaneous -> Data Asset, and assign the actions appropriately.
To tie everything up, open the character blueprint, my is BP_ThirdPersonCharacter, and assign the Input Mapping and Input Config objects.
And VOLA! Playtest it and everything should be working as intended. If your mouse’s vertical input is inverted, open Project Settings->Input
and uncheck Enable Legacy Input Scales.
Conclusion
The new Enhanced Input system will take time to get used to, in terms of setting it up in C++. But once it’s there, this new system could give more possibility to what type of input setup could be created, and inter-change at runtime. I hope this little tutorial gives a foundational understanding of how it works in C++ and how to setup it up quickly.
Please leave any feedback in the comment section, and have fun coding, fellow game programmers.
Leave a comment