Match Epic's C++ conventions so code reads like the engine and the rest of the project. The overriding rule: match the surrounding code first; these are the defaults when nothing else is established.
Related skills: cpp-fundamentals, core-types-and-containers, module-and-build-system.
When to use this skill
- Writing any new UE C++ header or source file.
- Reviewing or cleaning up code for style consistency.
- Choosing names for types, members, functions, or files.
- Structuring includes and forward declarations in a header.
Type prefixes (mandatory for reflected types)
UHT enforces prefixes for reflected types; mismatches are a compile error.
| Prefix | Type | Engine example |
|---|---|---|
U |
UObject subclass (non-actor) | UActorComponent (Components/ActorComponent.h:160) |
A |
AActor subclass | AActor (GameFramework/Actor.h:282) |
F |
Plain struct or non-UObject class | FAttachmentTransformRules (Engine/EngineTypes.h:75) |
E |
Enum / enum class | EAttachmentRule (Engine/EngineTypes.h:62) |
I |
Abstract interface class | IInterface_AssetUserData (Interfaces/Interface_AssetUserData.h:22) |
T |
Class template | TArray (Containers/Array.h:767) |
S |
Slate widget | SWidget, SCompoundWidget |
b |
Boolean variable | bReplicates (GameFramework/Actor.h:593) |
The word after the prefix is PascalCase. The class name without its prefix must match the
filename: AMyPawn → MyPawn.h. Typedefs take the prefix appropriate to their underlying type.
Full prefix rules, interface pairing, enum value style, and template parameter conventions: references/naming-conventions.md.
Naming
- PascalCase for every identifier — types, functions, member variables, local variables,
and parameters. No
m_prefix, nocamelCase, nosnake_case. - Booleans carry the
bprefix:bIsDead,bHasKey,bReplicates. - Functions with a bool return ask a question:
IsAlive(),ShouldClearBuffer(). - Output reference parameters carry
Out:void GetItems(TArray<FItem>& OutItems). - Type and variable names are nouns; function names are verb phrases.
- Be descriptive; avoid abbreviations except established ones (
AI,HUD,LOD,GC). - Macros are
UE_ALL_CAPS_WITH_UNDERSCORES.
Formatting
Allman braces — opening brace on its own line for every construct. Always brace single-statement blocks:
void AMyActor::BeginPlay()
{
Super::BeginPlay();
if (bIsReady)
{
DoThing();
}
}
- Tabs (4-character width) for indentation; spaces only for alignment within a line.
- One statement per line.
- Pointer/reference spacing:
FType* Ptr;andconst FType& Ref;—*/&bind to the type. - No variable shadowing across scopes.
Switch statements must have an explicit default: branch and document intentional fall-through
with // falls through.
Full formatting rules, switch style, and namespace rules: references/formatting-and-includes.md.
Language conventions
nullptr— neverNULLor0for pointers.overrideon every overriding virtual. Addfinalwhere the class or function should not be further overridden.constcorrectness — const member functions for non-mutating methods;const&for non-trivial parameters not being copied; never const a by-value return.TEXT("...")around every string literal that constructs anFStringorFName.enum classover plain enums; back withuint8if exposed to Blueprints. Values are PascalCase. UseENUM_CLASS_FLAGS(EFoo)for bitfield enums with aNone = 0sentinel.- Engine containers (
TArray,TMap,TSet,FString,FName) overstd::equivalents in engine-facing code. autoonly where the type is either a lambda, a verbose iterator, or genuinely indiscernible from context. Always applyconst,&, or*explicitly withauto.- Range-based for is preferred. For
TMap, iterate asfor (TPair<K,V>& Kvp : Map). - Move semantics — use
MoveTemp(X)(UE'sstd::move) when transferring ownership of containers orFStringinto a member or return. - Lambdas — prefer explicit captures over
[=]or[&]. CapturedUObject*pointers are invisible to the GC. UseCreateWeakLambda/BindWeakLambdafor deferred lambdas. - UObjects via pointer — pass by pointer, not reference. Null is the signal for "absent".
- Portable integer types:
int32,uint32,uint8,float,double,TCHAR; avoid bareintin serialized or replicated data.
Headers and includes
Header structure (in order)
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h" // 1. CoreMinimal (or fine-grained core headers)
#include "GameFramework/Actor.h" // 2. Engine / module headers this type needs
#include "MyActor.generated.h" // 3. generated.h — ALWAYS LAST
Source file structure
#include "MyActor.h" // matching header first
#include "Components/StaticMeshComponent.h" // then any implementation deps
#pragma onceat the top of every header (all target compilers support it).generated.hmust be the last include — UHT requires it. Missing it or putting it in the middle causes broken generated code.- IWYU (Include What You Use) — include every header you directly depend on; do not rely on transitive includes through another header.
- Forward declare in headers where you only need a pointer or reference. In
.cpp, include the full header. This reduces compile times and dependency coupling.
// Header — forward declare only
class UStaticMeshComponent;
UCLASS()
class MYGAME_API AMyActor : public AActor
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere) TObjectPtr<UStaticMeshComponent> Mesh;
};
// Source — full include
#include "Components/StaticMeshComponent.h"
Engine evidence: Actor.h lines 5–32 use IWYU-style fine-grained includes ending with
"Actor.generated.h" at line 32; Character.h lines 5–19 show CoreMinimal.h first and
"Character.generated.h" at line 19.
Reflection style
Every reflected class or struct needs GENERATED_BODY() as its first body member. Every
public class in a module needs the module *_API export macro:
UCLASS(Blueprintable, BlueprintType, config=Game)
class MYGAME_API AWeapon : public AActor
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category="Weapon")
void Fire();
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Stats",
meta=(ClampMin="0"))
float Damage = 25.f;
protected:
UPROPERTY(VisibleAnywhere, Category="Components")
TObjectPtr<UStaticMeshComponent> Mesh;
};
Key rules:
- Always set a
Categoryon anyUPROPERTY/UFUNCTIONthe editor will show. Without it, properties land in an uncategorized root group. - Put
meta=(...)last in the specifier list. - Use
TObjectPtr<T>forUPROPERTYmembers holdingUObject-derived pointers (UE5+ modern form). RawT*still works and appears in older code. - The
*_APImacro (e.g.MYGAME_API,ENGINE_API) must appear betweenclassand the class name for any non-inline public API. UBT expands it to__declspec(dllexport/import).
See references/reflection-and-uht.md for the full specifier
reference, USTRUCT/UENUM/UFUNCTION details, and common UHT errors.
Comments and documentation
//for inline comments;/** ... */JavaDoc-style comments on public API declarations — these surface as tooltips for Blueprint-exposed members and in the generated API docs.- Comment why, not what. Keep comments accurate and current with the code.
- Class comments describe the problem the class solves. Multi-line method comments document
purpose, parameter units/ranges, return value, and any
@warning/@see/@deprecated.
/** Maximum health this actor can have. Modified by difficulty at BeginPlay. */
UPROPERTY(EditDefaultsOnly, Category="Health", meta=(ClampMin="1"))
float MaxHealth = 100.f;
Logging and errors
- Log through a named category with
UE_LOG(logging-and-assertions). check(Condition)for invariants — aborts in all builds if violated. Never put side effects inside acheck.ensure(Condition)for recoverable "shouldn't happen" — fires once in non-shipping builds, returns bool so you can handle the failure.- Remove debug prints before submitting.
Gotchas
- Wrong or missing type prefix — UHT build error; check U/A/F/E/I/T/S.
generated.hnot last — UHT mis-generates or fails outright.- Missing
GENERATED_BODY()— compile errors from undefined generated symbols. - Missing
Categoryon editor-exposed properties — unorganized Details panel. m_prefix orsnake_case— not Unreal style; use PascalCase.- Missing
bon booleans — style violation; also breaks naming-based tooling. std::containers in engine-facing code — useTArray/TMap/FStringinstead.- Omitting
override— silent non-override when a virtual signature drifts. - Bare
NULL/0for pointers — usenullptr. - No
TEXT()around string literals — produces an undesirable narrow-to-wide conversion. autooveruse — hide types from readers; use only for lambdas, verbose iterators, or template-context expressions where the type is genuinely unwriteable.[=]/[&]lambda captures — UObject pointers captured by[=]are invisible to the GC; deferred[&]lambdas dangle. Use explicit captures and weak wrappers.
References and source material
Engine source (UE 5.8, under Engine/Source/):
Runtime/Engine/Classes/GameFramework/Actor.h:281–282, 288, 306, 593, 891, 1019, 1024Runtime/Engine/Classes/GameFramework/Character.h:3, 5, 19, 337–338Runtime/Engine/Classes/Components/ActorComponent.h:3, 23, 27–38, 159–162, 177, 340Runtime/Engine/Classes/Engine/EngineTypes.h:62, 75Runtime/Engine/Classes/Interfaces/Interface_AssetUserData.h:3, 16–22Runtime/CoreUObject/Public/UObject/Object.h:97–100, 106, 129Runtime/Core/Public/Containers/Array.h:767Runtime/Core/Public/Windows/WindowsPlatform.h:209–210Runtime/Core/Public/HAL/Platform.h:1063–1065
Official docs (UE 5.8):
- Epic C++ Coding Standard: https://dev.epicgames.com/documentation/unreal-engine/epic-cplusplus-coding-standard-for-unreal-engine
Deep-dive references in this skill:
- references/naming-conventions.md — all prefix rules, PascalCase details, boolean/enum/function/macro naming, interface pairing.
- references/formatting-and-includes.md — Allman
braces, tabs, switch style, const correctness detail,
#pragma once, include order, IWYU, forward declarations, API export macros. - references/reflection-and-uht.md —
UCLASS/USTRUCT/UENUM/UPROPERTY/UFUNCTIONspecifier reference,TObjectPtr,GENERATED_BODY(), and common UHT errors.