Skip to content

Code Examples

Complete implementation examples for the platformer system components.

Character Class (APlatformCharacter)

Header Declaration

cpp
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
#include "PlatformCharacter.generated.h"

class USpringArmComponent;
class UCameraComponent;
class UInputMappingContext;
class UInputAction;
class UCharacterTuningData;

UCLASS()
class PLATFORMER_API APlatformCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	APlatformCharacter();

protected:
	virtual void BeginPlay() override;
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	// Input Actions
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
	UInputMappingContext* DefaultMappingContext;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
	UInputAction* JumpAction;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
	UInputAction* MoveAction;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
	UInputAction* LookAction;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
	UInputAction* SprintAction;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
	UInputAction* InteractAction;

	// Tuning Data
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Tuning)
	UCharacterTuningData* TuningData;

	// Input Functions
	void Move(const FInputActionValue& Value);
	void Look(const FInputActionValue& Value);
	void StartSprint();
	void StopSprint();
	void Interact();

private:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
	USpringArmComponent* CameraBoom;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
	UCameraComponent* FollowCamera;

	bool bIsSprinting = false;

public:
	FORCEINLINE USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
	FORCEINLINE UCameraComponent* GetFollowCamera() const { return FollowCamera; }
};

Constructor Implementation

cpp
APlatformCharacter::APlatformCharacter()
{
	// Set size for collision capsule
	GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

	// Configure character rotation (don't rotate with controller for platformer)
	bUseControllerRotationPitch = false;
	bUseControllerRotationYaw = false;
	bUseControllerRotationRoll = false;

	// Configure character movement
	GetCharacterMovement()->bOrientRotationToMovement = true;
	GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f);
	GetCharacterMovement()->JumpZVelocity = 700.f;
	GetCharacterMovement()->AirControl = 0.35f;
	GetCharacterMovement()->MaxWalkSpeed = 600.0f;
	GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
	GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;
	GetCharacterMovement()->JumpMaxCount = 2; // Double jump

	// Create camera boom
	CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
	CameraBoom->SetupAttachment(RootComponent);
	CameraBoom->TargetArmLength = 450.0f;
	CameraBoom->bUsePawnControlRotation = false;
	CameraBoom->SetRelativeRotation(FRotator(-15.0f, 0.0f, 0.0f));

	// Create follow camera
	FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
	FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
	FollowCamera->bUsePawnControlRotation = false;
}

Interaction System

IInteractable Interface

cpp
#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "Interactable.generated.h"

UINTERFACE(MinimalAPI, Blueprintable)
class UInteractable : public UInterface
{
	GENERATED_BODY()
};

class PLATFORMER_API IInteractable
{
	GENERATED_BODY()

public:
	// Get the verb to display in UI (e.g., "Open", "Pickup", "Talk")
	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Interaction")
	FText GetInteractionVerb() const;

	// Get the display name of this object
	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Interaction")
	FText GetInteractionName() const;

	// Check if this object can be interacted with by the given instigator
	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Interaction")
	bool CanInteract(AActor* Instigator) const;

	// Perform the interaction
	UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Interaction")
	void Interact(AActor* Instigator);
};

UInteractionComponent

cpp
#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "InteractionComponent.generated.h"

class IInteractable;

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class PLATFORMER_API UInteractionComponent : public UActorComponent
{
	GENERATED_BODY()

public:
	UInteractionComponent();

protected:
	virtual void BeginPlay() override;
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
	float InteractionRange = 250.0f;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
	float InteractionRadius = 60.0f;

	UPROPERTY(BlueprintReadOnly, Category = "Interaction")
	AActor* CurrentFocusActor = nullptr;

public:
	UFUNCTION(BlueprintCallable, Category = "Interaction")
	void TryInteract();

	UFUNCTION(BlueprintCallable, Category = "Interaction")
	AActor* GetCurrentFocusActor() const { return CurrentFocusActor; }

private:
	void UpdateFocusActor();
	AActor* FindBestInteractable();
};

Interaction Component Implementation

cpp
#include "InteractionComponent.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "Camera/CameraComponent.h"
#include "Interactable.h"

UInteractionComponent::UInteractionComponent()
{
	PrimaryComponentTick.bCanEverTick = true;
	PrimaryComponentTick.TickInterval = 0.1f; // Check 10 times per second
}

void UInteractionComponent::BeginPlay()
{
	Super::BeginPlay();
}

void UInteractionComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
	UpdateFocusActor();
}

void UInteractionComponent::UpdateFocusActor()
{
	AActor* NewFocusActor = FindBestInteractable();

	if (NewFocusActor != CurrentFocusActor)
	{
		CurrentFocusActor = NewFocusActor;
		// Trigger UI update event here
	}
}

AActor* UInteractionComponent::FindBestInteractable()
{
	APawn* OwnerPawn = Cast<APawn>(GetOwner());
	if (!OwnerPawn)
		return nullptr;

	APlayerController* PC = Cast<APlayerController>(OwnerPawn->GetController());
	if (!PC)
		return nullptr;

	// Get camera location and forward direction
	FVector CameraLocation = PC->PlayerCameraManager->GetCameraLocation();
	FVector ForwardDirection = PC->PlayerCameraManager->GetCameraRotation().Vector();

	// Sphere trace for interactables
	FCollisionQueryParams QueryParams;
	QueryParams.AddIgnoredActor(OwnerPawn);

	TArray<FHitResult> HitResults;
	FVector TraceEnd = CameraLocation + (ForwardDirection * InteractionRange);

	bool bHit = GetWorld()->SweepMultiByChannel(
		HitResults,
		CameraLocation,
		TraceEnd,
		FQuat::Identity,
		ECC_Visibility,
		FCollisionShape::MakeSphere(InteractionRadius),
		QueryParams
	);

	if (!bHit)
		return nullptr;

	// Find best interactable (closest to camera center)
	AActor* BestActor = nullptr;
	float BestDot = -1.0f;

	for (const FHitResult& Hit : HitResults)
	{
		if (Hit.GetActor() && Hit.GetActor()->Implements<UInteractable>())
		{
			// Check if we can interact
			if (IInteractable::Execute_CanInteract(Hit.GetActor(), OwnerPawn))
			{
				// Calculate how "centered" this actor is in our view
				FVector ToActor = (Hit.GetActor()->GetActorLocation() - CameraLocation).GetSafeNormal();
				float DotProduct = FVector::DotProduct(ForwardDirection, ToActor);

				if (DotProduct > BestDot)
				{
					BestDot = DotProduct;
					BestActor = Hit.GetActor();
				}
			}
		}
	}

	return BestActor;
}

void UInteractionComponent::TryInteract()
{
	if (CurrentFocusActor && CurrentFocusActor->Implements<UInteractable>())
	{
		IInteractable::Execute_Interact(CurrentFocusActor, GetOwner());
	}
}

Stats Component

UStatsComponent Header

cpp
#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "StatsComponent.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnHealthChanged, float, CurrentHealth, float, MaxHealth, float, HealthPercentage);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnDeath, AActor*, Killer, AActor*, Victim);

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class PLATFORMER_API UStatsComponent : public UActorComponent
{
	GENERATED_BODY()

public:
	UStatsComponent();

protected:
	virtual void BeginPlay() override;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Stats")
	float MaxHealth = 100.0f;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Stats")
	float MaxStamina = 100.0f;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Stats")
	float HealthRegenRate = 0.0f; // Per second

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Stats")
	float HealthRegenDelay = 3.0f; // Seconds after taking damage

	UPROPERTY(BlueprintReadOnly, Category = "Stats")
	float CurrentHealth;

	UPROPERTY(BlueprintReadOnly, Category = "Stats")
	float CurrentStamina;

private:
	FTimerHandle HealthRegenTimerHandle;
	float LastDamageTime = 0.0f;

public:
	UPROPERTY(BlueprintAssignable, Category = "Events")
	FOnHealthChanged OnHealthChanged;

	UPROPERTY(BlueprintAssignable, Category = "Events")
	FOnDeath OnDeath;

	UFUNCTION(BlueprintCallable, Category = "Stats")
	float ApplyDamage(float DamageAmount, AActor* DamageCauser = nullptr);

	UFUNCTION(BlueprintCallable, Category = "Stats")
	void RestoreHealth(float HealAmount);

	UFUNCTION(BlueprintCallable, Category = "Stats")
	bool IsAlive() const { return CurrentHealth > 0.0f; }

	UFUNCTION(BlueprintCallable, Category = "Stats")
	float GetHealthPercentage() const { return MaxHealth > 0 ? CurrentHealth / MaxHealth : 0.0f; }

private:
	void StartHealthRegen();
	void TickHealthRegen();
};

Data Assets

Character Tuning Data

cpp
#pragma once

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "CharacterTuningData.generated.h"

UCLASS(BlueprintType)
class PLATFORMER_API UCharacterTuningData : public UPrimaryDataAsset
{
	GENERATED_BODY()

public:
	// Movement settings
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Movement", meta = (ClampMin = "100", ClampMax = "1000"))
	float WalkSpeed = 600.0f;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Movement", meta = (ClampMin = "200", ClampMax = "1500"))
	float SprintSpeed = 900.0f;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Movement", meta = (ClampMin = "300", ClampMax = "1200"))
	float JumpZVelocity = 700.0f;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Movement", meta = (ClampMin = "0.1", ClampMax = "1.0"))
	float AirControl = 0.35f;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Movement", meta = (ClampMin = "1", ClampMax = "3"))
	int32 JumpMaxCount = 2;

	// Camera settings
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Camera", meta = (ClampMin = "200", ClampMax = "800"))
	float CameraDistance = 450.0f;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Camera", meta = (ClampMin = "1", ClampMax = "25"))
	float CameraLagSpeed = 12.0f;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Camera", meta = (ClampMin = "1", ClampMax = "25"))
	float CameraRotationLagSpeed = 10.0f;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Camera", meta = (ClampMin = "-45", ClampMax = "45"))
	float CameraAngle = -15.0f;
};

Enhanced Input Setup

Input Action Creation (Blueprint)

cpp
// This would be created in Blueprint, but here's how to reference them in C++

// In your character's header:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
UInputAction* MoveAction;

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
UInputAction* LookAction;

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
UInputAction* JumpAction;

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
UInputAction* SprintAction;

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
UInputAction* InteractAction;

// In SetupPlayerInputComponent:
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
	EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump);
	EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);

	EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &APlatformCharacter::Move);
	EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &APlatformCharacter::Look);

	EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Started, this, &APlatformCharacter::StartSprint);
	EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Completed, this, &APlatformCharacter::StopSprint);

	EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Triggered, this, &APlatformCharacter::Interact);
}

Common Patterns

Safe Casting Pattern

cpp
// Always use safe casting for UObjects
if (APawn* OwnerPawn = Cast<APawn>(GetOwner()))
{
	if (APlayerController* PC = Cast<APlayerController>(OwnerPawn->GetController()))
	{
		// Safe to use PC here
	}
}

Component Access Pattern

cpp
// Safe component access
if (UStatsComponent* StatsComp = GetOwner()->FindComponentByClass<UStatsComponent>())
{
	StatsComp->ApplyDamage(DamageAmount);
}

Interface Implementation Check

cpp
// Check if actor implements interface
if (TargetActor && TargetActor->Implements<UInteractable>())
{
	// Safe to call interface functions
	IInteractable::Execute_Interact(TargetActor, this);
}

Timer Usage Pattern

cpp
// Setting up a timer
FTimerHandle TimerHandle;
GetWorld()->GetTimerManager().SetTimer(
	TimerHandle,
	this,
	&UMyComponent::MyTimerFunction,
	1.0f,  // Delay
	true   // Looping
);

// Clearing a timer
GetWorld()->GetTimerManager().ClearTimer(TimerHandle);

These examples provide the foundation for implementing all the milestone features. Each example includes proper UE5 patterns, memory safety, and Blueprint integration points.

This page provides minimal C++ code snippets for key concepts.

Enhanced Input Setup

Input Action Asset setup (in editor):

  • Create Input Action IA_Move (Value Type: Axis2D)
  • Create Input Action IA_Jump (Value Type: Digital Bool)
  • Create Input Mapping Context IMC_Player

Character header snippet:

cpp
UCLASS()
class PLATFORMER_API APlatformCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    APlatformCharacter();

protected:
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
    class USpringArmComponent* CameraBoom;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
    class UCameraComponent* FollowCamera;

    UPROPERTY(EditDefaultsOnly, Category = Input)
    class UInputMappingContext* DefaultMappingContext;

    UPROPERTY(EditDefaultsOnly, Category = Input)
    class UInputAction* MoveAction;

    UPROPERTY(EditDefaultsOnly, Category = Input)
    class UInputAction* JumpAction;

protected:
    virtual void BeginPlay() override;
    virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;

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

Interface Example

IInteractable interface:

cpp
UINTERFACE(MinimalAPI, BlueprintType)
class UInteractable : public UInterface
{
    GENERATED_BODY()
};

class PLATFORMER_API IInteractable
{
    GENERATED_BODY()

public:
    virtual FText GetInteractionVerb() const = 0;
    virtual bool CanInteract(AActor* Instigator) const = 0;
    virtual void Interact(AActor* Instigator) = 0;
};

Data Asset Example

Character tuning data asset:

cpp
UCLASS()
class PLATFORMER_API UCharacterTuningData : public UPrimaryDataAsset
{
    GENERATED_BODY()

public:
    UPROPERTY(EditDefaultsOnly, Category = "Movement")
    float WalkSpeed = 600.0f;

    UPROPERTY(EditDefaultsOnly, Category = "Movement")
    float SprintSpeed = 900.0f;

    UPROPERTY(EditDefaultsOnly, Category = "Movement")
    float AirControl = 0.5f;

    UPROPERTY(EditDefaultsOnly, Category = "Movement")
    int32 JumpMaxCount = 2;

    virtual FPrimaryAssetId GetPrimaryAssetId() const override;
};

Component Example

Stats component header:

cpp
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class PLATFORMER_API UStatsComponent : public UActorComponent
{
    GENERATED_BODY()

public:
    UStatsComponent();

    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnDeath, AActor*, Victim, AActor*, Killer);

    UPROPERTY(BlueprintAssignable)
    FOnDeath OnDeath;

protected:
    UPROPERTY(EditDefaultsOnly, Category = "Stats")
    float HealthMax = 100.0f;

    UPROPERTY(BlueprintReadOnly, Category = "Stats")
    float Health = 100.0f;

public:
    UFUNCTION(BlueprintCallable, Category = "Stats")
    float ApplyDamage(float DamageAmount, AActor* DamageCauser);

    UFUNCTION(BlueprintPure, Category = "Stats")
    float GetHealthPercent() const { return Health / HealthMax; }
};