Unreal Engine 5 skill for Claude Code

C++ foundations, Blueprint flexibility, clean cooks. Claude decides what belongs in C++ versus Blueprint, places state on the right gameplay framework class, and keeps every UObject pointer visible to the garbage collector. It wires Enhanced Input correctly, avoids hard-reference load chains, writes replication with validated server RPCs, and packages builds with BuildCookRun and headless automation tests.

  • nameunreal-engine
  • size243 lines · ≈2,450 tokens
  • files1
  • licenseMIT
  • updated

What Claude sees before loading it

This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.

description: Builds Unreal Engine 5 gameplay with a deliberate Blueprint and C++ split, the gameplay framework (GameMode, GameState, PlayerController, Pawn, subsystems), UPROPERTY and UFUNCTION reflection, Enhanced Input, replication basics, asset reference hygiene, and BuildCookRun packaging. Use when writing UE5 C++ classes or Blueprints, deciding what belongs in C++, fixing garbage-collected pointers or crashes, wiring input, cutting load times caused by hard references, or packaging a build for Windows, Linux, consoles, or mobile.

Try asking

  • Port this character Blueprint's movement and health logic to a C++ base class
  • The game crashes randomly after a few minutes, check our UObject pointers
  • Write a BuildCookRun script that packages a Shipping Win64 build in CI

Use it when

  • Writing UE5 C++ classes or deciding what to move out of Blueprints
  • Random crashes that smell like garbage-collected pointers
  • Setting up Enhanced Input or basic replication
  • Packaging builds or fixing cook errors

Skip it when

  • Netcode theory such as prediction and rollback across engines — use multiplayer-netcode
  • General modern C++ outside Unreal's reflection system — use cpp-modern

How to check the result

The module compiles without new warnings, PIE with multiple clients logs no new errors, and BuildCookRun produces a launching packaged build.

The common mistake it prevents

Holding a raw UObject* member without UPROPERTY(), which the garbage collector cannot see, leading to dangling pointers and intermittent crashes.

~/.claude/skills/unreal-engine/SKILL.md

--- name: unreal-engine description: Builds Unreal Engine 5 gameplay with a deliberate Blueprint and C++ split, the gameplay framework (GameMode, GameState, PlayerController,… license: MIT ---

Done well means: core systems and anything performance-sensitive or replicated live in C++, designers tune and script content in Blueprints derived from those classes, every UObject pointer is visible to the garbage collector, and the project packages from the command line with zero cook errors.

Ground rules

  • Target UE 5.4+ (Enhanced Input default, TObjectPtr, World Partition). Read the .uproject (EngineAssociation, modules, plugins) and Source/<Module>/<Module>.Build.cs before adding code.
  • Never move, rename, or delete .uasset files outside the editor. Use the Content Browser, then run Fix Up Redirectors on the folder.
  • Header changes that add or remove UPROPERTY/UFUNCTION/UCLASS need a full editor restart and rebuild; Live Coding handles function-body edits only.
  • .uasset and .umap are binary: use Git LFS with file locking or Perforce. Enable One File Per Actor on World Partition maps to reduce level conflicts.

Blueprint or C++

Put it in C++Put it in Blueprint
Base classes for characters, weapons, abilitiesChild classes that set meshes, sounds, tuning values
Per-frame math, loops over many actors, AI queriesOne-off level scripting, cinematics triggers
Replication logic, RPCs, save systemsUI layout and animation (UMG) wiring
Interfaces and data structs used across systemsRapid prototypes, later promoted to C++ if they stick
Anything that needs code review and diffingTuning that designers iterate on daily

Expose to Blueprint deliberately: BlueprintCallable for actions, BlueprintPure for side-effect-free getters, BlueprintImplementableEvent for designer hooks, and BlueprintNativeEvent (implemented in _Implementation) when C++ supplies a default.

Gameplay framework

ClassLives onHolds
AGameModeBase / AGameModeServer onlyRules, spawning, match flow. Clients cannot access it
AGameStateBaseServer + replicated to allMatch state everyone sees (score, timer)
APlayerControllerServer + owning clientInput routing, UI ownership, camera
APlayerStateReplicated to allPer-player public data (name, team, score); survives pawn death
APawn / ACharacterReplicatedThe body: movement, health, abilities
UGameInstanceEach process, persists across levelsSession-wide data, platform login
Subsystems (UGameInstanceSubsystem, UWorldSubsystem, ULocalPlayerSubsystem)Auto-created with their ownerServices without singleton boilerplate

Put data where its lifetime matches: health on the Pawn, kills on PlayerState, round timer on GameState, settings on a GameInstance or LocalPlayer subsystem.

C++ patterns

// MyCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"

class UInputAction;
class UInputMappingContext;
struct FInputActionValue;

UCLASS()
class MYGAME_API AMyCharacter : public ACharacter
{
    GENERATED_BODY()
public:
    AMyCharacter();

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

    UPROPERTY(EditDefaultsOnly, Category = "Input")
    TObjectPtr<UInputMappingContext> DefaultContext;

    UPROPERTY(EditDefaultsOnly, Category = "Input")
    TObjectPtr<UInputAction> MoveAction;

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Stats", meta = (ClampMin = "0"))
    float MaxHealth = 100.f;

private:
    void Move(const FInputActionValue& Value);
};
// MyCharacter.cpp
#include "MyCharacter.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputActionValue.h"

AMyCharacter::AMyCharacter()
{
    PrimaryActorTick.bCanEverTick = false;   // enable only if Tick is really needed
}

void AMyCharacter::BeginPlay()
{
    Super::BeginPlay();
    if (const APlayerController* PC = Cast<APlayerController>(GetController()))
    {
        if (auto* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
        {
            Subsystem->AddMappingContext(DefaultContext, /*Priority*/ 0);
        }
    }
}

void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    if (auto* EIC = Cast<UEnhancedInputComponent>(PlayerInputComponent))
    {
        EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
    }
}

void AMyCharacter::Move(const FInputActionValue& Value)
{
    const FVector2D Axis = Value.Get<FVector2D>();
    AddMovementInput(GetActorForwardVector(), Axis.Y);
    AddMovementInput(GetActorRightVector(), Axis.X);
}

Add "EnhancedInput" to PublicDependencyModuleNames in Build.cs. If the pawn can be possessed later (respawn, vehicles), add the mapping context in PawnClientRestart or the controller instead of BeginPlay.

Memory and pointers

PointerUse when
UPROPERTY() TObjectPtr<T>Owning or strong reference to a UObject member. Keeps it alive for GC
TWeakObjectPtr<T>Non-owning reference that may be destroyed (targets, listeners)
TSoftObjectPtr<T> / TSoftClassPtr<T>Asset referenced but loaded on demand (async via FStreamableManager or Asset Manager)
TSharedPtr / TUniquePtrPlain C++ (non-UObject) data only
  • A raw UObject* member without UPROPERTY() is invisible to GC and dangles after collection. This is the top cause of random crashes.
  • IsValid(Obj) checks null and pending-kill; use it before dereferencing actors that can be destroyed.
  • CreateDefaultSubobject only in constructors. Constructors also run for the Class Default Object, so no gameplay logic, world access, or spawning there; use BeginPlay.

Asset references and load times

  • A Blueprint’s hard references (variables typed as a class or asset, casts to Blueprint classes) load the whole chain with it. Check Reference Viewer and Size Map on heavy Blueprints.
  • Break chains with soft references, interfaces instead of casts, and C++ base classes for casting.
  • Naming prefixes keep the Content Browser searchable: BP_, ABP_, WBP_, SM_, SK_, M_, MI_, T_, NS_, DA_ (data assets).
  • Data Assets (UPrimaryDataAsset) and Data Tables for designer-tuned data.

Replication basics

UPROPERTY(ReplicatedUsing = OnRep_Health)
float Health;

UFUNCTION()
void OnRep_Health();

UFUNCTION(Server, Reliable)
void ServerFire(const FVector_NetQuantize& Aim);

void AMyCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out) const
{
    Super::GetLifetimeReplicatedProps(Out);
    DOREPLIFETIME(AMyCharacter, Health);
}
  • Include Net/UnrealNetwork.h; set bReplicates = true on the actor.
  • Server RPCs run only when called by the owning client; clients cannot call them on actors they do not own.
  • Reliable only for rare, important events; cosmetic or high-frequency calls use Unreliable. Validate inputs on the server (clamp, cooldown checks).
  • Abilities with prediction, cooldowns, and attributes: use the Gameplay Ability System rather than hand-rolled RPCs. For netcode theory see multiplayer-netcode.

Ticking and timers

  • Disable tick by default; use FTimerManager (GetWorldTimerManager().SetTimer) for periodic logic and set PrimaryActorTick.TickInterval when tick is needed less often.
  • Tick groups matter for ordering against physics (TG_PrePhysics, TG_PostPhysics).

Logging and assertions

DECLARE_LOG_CATEGORY_EXTERN(LogMyGame, Log, All);   // header
DEFINE_LOG_CATEGORY(LogMyGame);                      // one .cpp
UE_LOG(LogMyGame, Warning, TEXT("Spawned %s at %s"), *GetName(), *GetActorLocation().ToString());
  • check() halts in Debug and Development builds (compiled out in Shipping); ensure() logs and continues once; verify() keeps the expression in Shipping.

Packaging

# Windows (use RunUAT.sh on Linux/macOS)
Engine/Build/BatchFiles/RunUAT.bat BuildCookRun -project="C:/Proj/MyGame.uproject" \
  -platform=Win64 -clientconfig=Shipping -build -cook -stage -pak -archive \
  -archivedirectory="C:/Builds/MyGame" -unattended -utf8output
  • Cook warnings about missing assets usually mean a soft reference not registered in the Asset Manager or maps not listed in Project Settings > Packaging.
  • Development builds for testing (-clientconfig=Development), Shipping for release.
  • Run automation tests headless: UnrealEditor-Cmd MyGame.uproject -ExecCmds="Automation RunTests MyGame; Quit" -unattended -nullrhi -nosplash.

Gotchas

  • GameMode does not exist on clients; client code calling GetAuthGameMode() gets null.
  • Hot Reload can corrupt Blueprints derived from changed classes; close the editor for header changes.
  • Cast<> to a Blueprint class creates a hard reference to that Blueprint asset.
  • Forgetting Super:: calls in BeginPlay, EndPlay, Tick, or GetLifetimeReplicatedProps silently breaks engine behavior.
  • Binding a delegate with AddDynamic requires the target function to be a UFUNCTION().
  • Blueprint “Event Tick” on hundreds of actors is a frame-time sink; profile with Unreal Insights (-trace=cpu,frame) before optimizing.

Verification

  1. C++ compiles from the IDE or Build.bat MyGameEditor Win64 Development with no new warnings; editor opens without “modules out of date”.
  2. Play In Editor with the Output Log filtered to Error/Warning shows nothing new; for networked code test with 2+ clients and Net Mode “Play As Client”.
  3. Automation tests pass headless.
  4. BuildCookRun finishes with zero cook errors and the packaged build launches.

About this skill

Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.

esc