Events Binding and raycast

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 from ACharacter) with:
    • USpringArmComponent + UCameraComponent
    • UInputMappingContext* DefaultMappingContext
    • UInputAction* JumpAction, MoveAction, LookAction
  • In BeginPlay() the MappingContext is 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 press
  • Triggered — 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 OnOverlapBegin signature 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:

  1. Create an Input Action in the editor (e.g. IA_Raycast, type bool).
  2. Add it to the Input Mapping Context (e.g. mapped to key E).
  3. In the header:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
UInputAction* RaycastAction;
  1. Bind it in SetupPlayerInputComponent:
EIC->BindAction(RaycastAction, ETriggerEvent::Started, this, &AMyProjectCharacter::PerformRaycast);
  1. Adjust the signature if needed (add const FInputActionValue& Value as 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:

  1. Content Browser → right click → Input → Input Action
  2. Open it, set Value Type (bool for button, Axis2D for stick…)
  3. Add it to the existing IMC, assign a key
  4. Declare UPROPERTY UInputAction* in the header
  5. Assign it in the editor (Details panel of the Character BP)
  6. 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"