UE5.4 — C++ Cheatsheet (Third Person Template)
0. What the Third Person C++ Template Gives You
When you create the project you already have:
AMyProjectCharacter(inherits fromACharacter) with:USpringArmComponent+UCameraComponentUInputMappingContext* DefaultMappingContextUInputAction* JumpAction, MoveAction, LookAction
- In
BeginPlay()theMappingContextis already registered in the Enhanced Input subsystem. - In
SetupPlayerInputComponent()Jump, Move and Look are already bound.
Includes that come with the template (and that you’ll need):
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputActionValue.h"
1. Binding a Custom Event on Jump (Enhanced Input)
The template already binds jump to ACharacter::Jump. To add custom logic (e.g. print log, spawn particles, count jumps…), create your own function and bind it instead of (or alongside) the default one.
Header (.h) — add to the Character class
// Jump counter (example)
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Stats")
int32 JumpCount = 0;
// Custom function called on jump
void HandleJump(const FInputActionValue& Value);
Source (.cpp) — in SetupPlayerInputComponent
Find where JumpAction is already bound and replace it:
void AMyProjectCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
if (UEnhancedInputComponent* EIC = Cast<UEnhancedInputComponent>(PlayerInputComponent))
{
// --- JUMP: bind to our custom function ---
EIC->BindAction(JumpAction, ETriggerEvent::Started, this, &AMyProjectCharacter::HandleJump);
EIC->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
// Move and Look stay the same...
EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyProjectCharacter::Move);
EIC->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyProjectCharacter::Look);
}
}
HandleJump Implementation
void AMyProjectCharacter::HandleJump(const FInputActionValue& Value)
{
Jump(); // calls ACharacter::Jump() (performs the actual jump)
JumpCount++;
UE_LOG(LogTemp, Warning, TEXT("Jump number: %d"), JumpCount);
// Add whatever is needed here: sound, particles, etc.
}
Key pattern: BindAction(InputAction, ETriggerEvent, Object, &Function)
Most common ETriggerEvent values:
Started— on pressTriggered— while held (axes, movement)Completed— on release
2. Pickup (Collectible Actor with Collision)
Create the class
New file: Add C++ Class → Actor → APickupActor
Header — PickupActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "PickupActor.generated.h"
UCLASS()
class MYPROJECT_API APickupActor : public AActor
{
GENERATED_BODY()
public:
APickupActor();
protected:
UPROPERTY(VisibleAnywhere)
UStaticMeshComponent* MeshComp;
UPROPERTY(VisibleAnywhere)
class USphereComponent* CollisionComp;
// Overlap callback
UFUNCTION()
void OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult);
};
Source — PickupActor.cpp
#include "PickupActor.h"
#include "Components/SphereComponent.h"
#include "GameFramework/Character.h" // for Cast
APickupActor::APickupActor()
{
// Collision sphere as root
CollisionComp = CreateDefaultSubobject<USphereComponent>(TEXT("CollisionComp"));
CollisionComp->SetSphereRadius(100.f);
CollisionComp->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
SetRootComponent(CollisionComp);
// Visual mesh
MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComp"));
MeshComp->SetupAttachment(CollisionComp);
MeshComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
// Bind overlap
CollisionComp->OnComponentBeginOverlap.AddDynamic(this, &APickupActor::OnOverlapBegin);
}
void APickupActor::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult)
{
// Only react if it's the player
if (ACharacter* Player = Cast<ACharacter>(OtherActor))
{
UE_LOG(LogTemp, Warning, TEXT("Pickup collected!"));
// Here: add score, heal, give item, etc.
Destroy();
}
}
Key points for the exam:
- The
OnOverlapBeginsignature must be exact (6 parameters). - It needs
UFUNCTION()above it. - Binding is done with
AddDynamic(this, &Class::Function). SetCollisionProfileName("OverlapAllDynamic")so it generates overlaps.- Assign a mesh in the editor (or via code with
ConstructorHelpers::FObjectFinder).
3. Raycast (Line Trace)
Can be done from anywhere. Most common: from the camera or from the character’s eyes.
From the player camera (most common in third person)
Add to the Character header:
void PerformRaycast();
Implementation:
void AMyProjectCharacter::PerformRaycast()
{
APlayerController* PC = Cast<APlayerController>(GetController());
if (!PC) return;
// Origin and direction from the center of the screen
FVector Start;
FVector Direction;
PC->DeprojectScreenPositionToWorld(
GEngine->GameViewport->Viewport->GetSizeXY().X / 2.f,
GEngine->GameViewport->Viewport->GetSizeXY().Y / 2.f,
Start, Direction
);
FVector End = Start + Direction * 5000.f; // 50 meters
FHitResult Hit;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this); // don't detect yourself
bool bHit = GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Visibility, Params);
if (bHit)
{
UE_LOG(LogTemp, Warning, TEXT("Hit: %s"), *Hit.GetActor()->GetName());
// Visual debug (green line = hit, red = miss)
DrawDebugLine(GetWorld(), Start, Hit.ImpactPoint, FColor::Green, false, 2.f);
DrawDebugPoint(GetWorld(), Hit.ImpactPoint, 10.f, FColor::Red, false, 2.f);
}
else
{
DrawDebugLine(GetWorld(), Start, End, FColor::Red, false, 2.f);
}
}
Simpler version: from the character’s position
void AMyProjectCharacter::PerformRaycast()
{
FVector Start = GetActorLocation();
FVector End = Start + GetActorForwardVector() * 5000.f;
FHitResult Hit;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Visibility, Params))
{
UE_LOG(LogTemp, Warning, TEXT("Hit: %s"), *Hit.GetActor()->GetName());
}
}
Triggering the raycast with a key (Enhanced Input)
If you need to activate the raycast with a key press:
- Create an Input Action in the editor (e.g.
IA_Raycast, typebool). - Add it to the Input Mapping Context (e.g. mapped to key
E). - In the header:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
UInputAction* RaycastAction;
- Bind it in
SetupPlayerInputComponent:
EIC->BindAction(RaycastAction, ETriggerEvent::Started, this, &AMyProjectCharacter::PerformRaycast);
- Adjust the signature if needed (add
const FInputActionValue& Valueas a parameter even if unused).
4. Quick Reminder — Enhanced Input Flow
Input Mapping Context (IMC)
└─ maps keys/axes → Input Actions (IA)
BeginPlay:
└─ Subsystem->AddMappingContext(IMC, priority)
SetupPlayerInputComponent:
└─ EIC->BindAction(IA, ETriggerEvent, this, &Callback)
To create a new IA:
- Content Browser → right click → Input → Input Action
- Open it, set Value Type (bool for button, Axis2D for stick…)
- Add it to the existing IMC, assign a key
- Declare
UPROPERTY UInputAction*in the header - Assign it in the editor (Details panel of the Character BP)
- Bind it in
SetupPlayerInputComponent
5. Quick Reference Includes
// Enhanced Input (usually already present)
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputActionValue.h"
// Collisions / Overlaps
#include "Components/SphereComponent.h"
#include "Components/BoxComponent.h"
// Raycast debug
#include "DrawDebugHelpers.h"
// Useful casts
#include "GameFramework/Character.h"
#include "GameFramework/PlayerController.h"
#include "Kismet/GameplayStatics.h"