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.
- name
unreal-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.
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) andSource/<Module>/<Module>.Build.csbefore adding code. - Never move, rename, or delete
.uassetfiles outside the editor. Use the Content Browser, then run Fix Up Redirectors on the folder. - Header changes that add or remove
UPROPERTY/UFUNCTION/UCLASSneed a full editor restart and rebuild; Live Coding handles function-body edits only. .uassetand.umapare 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, abilities | Child classes that set meshes, sounds, tuning values |
| Per-frame math, loops over many actors, AI queries | One-off level scripting, cinematics triggers |
| Replication logic, RPCs, save systems | UI layout and animation (UMG) wiring |
| Interfaces and data structs used across systems | Rapid prototypes, later promoted to C++ if they stick |
| Anything that needs code review and diffing | Tuning 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
| Class | Lives on | Holds |
|---|---|---|
AGameModeBase / AGameMode | Server only | Rules, spawning, match flow. Clients cannot access it |
AGameStateBase | Server + replicated to all | Match state everyone sees (score, timer) |
APlayerController | Server + owning client | Input routing, UI ownership, camera |
APlayerState | Replicated to all | Per-player public data (name, team, score); survives pawn death |
APawn / ACharacter | Replicated | The body: movement, health, abilities |
UGameInstance | Each process, persists across levels | Session-wide data, platform login |
Subsystems (UGameInstanceSubsystem, UWorldSubsystem, ULocalPlayerSubsystem) | Auto-created with their owner | Services 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
| Pointer | Use 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 / TUniquePtr | Plain C++ (non-UObject) data only |
- A raw
UObject*member withoutUPROPERTY()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.CreateDefaultSubobjectonly in constructors. Constructors also run for the Class Default Object, so no gameplay logic, world access, or spawning there; useBeginPlay.
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; setbReplicates = trueon the actor. - Server RPCs run only when called by the owning client; clients cannot call them on actors they do not own.
Reliableonly for rare, important events; cosmetic or high-frequency calls useUnreliable. 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 setPrimaryActorTick.TickIntervalwhen 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
GameModedoes not exist on clients; client code callingGetAuthGameMode()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 inBeginPlay,EndPlay,Tick, orGetLifetimeReplicatedPropssilently breaks engine behavior. - Binding a delegate with
AddDynamicrequires the target function to be aUFUNCTION(). - Blueprint “Event Tick” on hundreds of actors is a frame-time sink; profile with
Unreal Insights (
-trace=cpu,frame) before optimizing.
Verification
- C++ compiles from the IDE or
Build.bat MyGameEditor Win64 Developmentwith no new warnings; editor opens without “modules out of date”. - 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”.
- Automation tests pass headless.
- 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.