Ue Cpp Foundations logo

Ue Cpp Foundations

Community
quodsoler
ue-cpp-foundations

Use when writing Unreal Engine C++ that touches the reflection system or core types: UCLASS, UPROPERTY, UFUNCTION, USTRUCT, UENUM, GENERATED_BODY, TObjectPtr, TWeakObjectPtr, TSharedPtr, FGCObject, TArray, TMap, TSet, TOptional, TVariant, DECLARE_DYNAMIC_MULTICAST_DELEGATE, AddDynamic, FName, FString, FText, UGameInstanceSubsystem, UWorldSubsystem, GetDefault, TNotNull. Also use when the user mentions 'UE C++', 'garbage collection', 'CDO', 'smart pointers' or 'specifiers'. For Actor and component lifecycle, see ue-actor-component-architecture; for Build.cs and modules, see ue-module-build-system; for Blueprint-facing functions and latent actions, see ue-blueprint-cpp-interop.

Overview

Publisherquodsoler
Repositoryunreal-engine-skills
Skill nameue-cpp-foundations
Stars
403
Forks
66
Bundled files
3
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 3 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by quodsoler on GitHub. Read the source before you install it.

Installation

Install the Ue Cpp Foundations AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/quodsoler/unreal-engine-skills/tree/main/skills/ue-cpp-foundations
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/quodsoler/unreal-engine-skills.git /tmp/unreal-engine-skills
mkdir -p .claude/skills
cp -r /tmp/unreal-engine-skills/skills/ue-cpp-foundations .claude/skills/ue-cpp-foundations
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Ue Cpp Foundations in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Ue Cpp Foundations on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Ue Cpp Foundations is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

UE C++ Foundations

Target engine: UE 5.8. APIs below are verified against the 5.8 headers; older forms are listed under "Deprecated — do not use".

This skill covers the C++ layer every Unreal module is built on: reflection macros (UCLASS, UPROPERTY, UFUNCTION, USTRUCT, UENUM, UINTERFACE), containers, delegates, string types, object lifetime and garbage collection, CDO access, and the subsystem family. All of it lives in the Core, CoreUObject and Engine modules (PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine" });). Editor subsystems additionally need EditorSubsystem and UnrealEd in an editor-only module.

Context

Read .agents/ue-project-context.md if it exists (module names, conventions, enabled plugins, GAS/networking setup). Do not stop if it is missing.

Identify the area from the request and the codebase. Ask only when two plausible readings would produce different code.

Request is about…Go to
UCLASS, UPROPERTY, UFUNCTION, USTRUCT, UENUM specifiersReflection Macros
TArray, TMap, TSet, TOptional, TVariantContainers
Delegates, events, AddDynamic, BroadcastDelegates
FName, FString, FText, LOCTEXTString Types
TObjectPtr, TWeakObjectPtr, TSharedPtr, GC roots, FGCObjectObject Lifetime and Pointers
Class default objects, GetDefault, TNotNull parametersCDO Access and Non-Null Parameters
Game instance / world / local player / engine / editor subsystemsSubsystems
Replicated, ReplicatedUsing, GetLifetimeReplicatedPropsReplicated Properties
UE_LOG, log categoriesLogging
WITH_EDITOR, UE_BUILD_SHIPPING guardsConditional Compilation Guards

Reflection Macros

Every reflected type has GENERATED_BODY() as the first line of its body and #include "TypeName.generated.h" as the last include of its header. Every UCLASS in a game module carries the module API macro (MYGAME_API). Specifier parsing is case-insensitive (engine headers use both config=Game and Config=Game). Complete specifier and meta= tables: references/property-specifiers.md.

UCLASS

SpecifierEffect
Blueprintable / BlueprintTypeAllow Blueprint subclasses / use as a Blueprint variable type (Not… forms block them)
AbstractCannot be instantiated; subclasses can
Config=Game, DefaultConfigUPROPERTY(Config) members load from DefaultGame.ini; DefaultConfig also saves there
TransientInstances are never saved
ClassGroup=Name + meta=(BlueprintSpawnableComponent)Component appears in the Add Component menu

UPROPERTY

GroupSpecifiers
Editor visibilityEditAnywhere, EditDefaultsOnly, EditInstanceOnly, VisibleAnywhere, VisibleDefaultsOnly, VisibleInstanceOnly, AdvancedDisplay
Blueprint accessBlueprintReadWrite, BlueprintReadOnly, BlueprintGetter=Fn, BlueprintSetter=Fn, BlueprintAssignable (multicast delegates only)
SerializationTransient, DuplicateTransient, SaveGame, Config, GlobalConfig, Instanced, SkipSerialization, NonTransactional
ReplicationReplicated, ReplicatedUsing=OnRep_Fn, NotReplicated
Frequent meta= keysClampMin/ClampMax, UIMin/UIMax, EditCondition, EditConditionHides, AllowPrivateAccess, DisplayName, ToolTip, Units, AllowedClasses, MustImplement, ExposeOnSpawn, TitleProperty
cpp
// MyCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"

class UStaticMeshComponent;
class UStaticMesh;

UCLASS(Blueprintable)
class MYGAME_API AMyCharacter : public ACharacter
{
    GENERATED_BODY()
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stats", meta=(ClampMin="1.0"))
    float MaxHealth = 100.f;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Stats")
    float CurrentHealth = 100.f;

    UPROPERTY(Transient)
    TObjectPtr<UStaticMeshComponent> CachedMesh;     // not serialized, still GC-tracked

    UPROPERTY(EditDefaultsOnly, Category="Config")
    TSubclassOf<AActor> ProjectileClass;             // class reference restricted to AActor subclasses
    UPROPERTY(EditDefaultsOnly, Category="Config")
    TSoftObjectPtr<UStaticMesh> LazyMesh;            // path reference; load on demand

    UFUNCTION(BlueprintNativeEvent, Category="Combat")
    void OnDamageTaken(float Amount);
    virtual void OnDamageTaken_Implementation(float Amount);
};

UFUNCTION

SpecifierEffect
BlueprintCallable / BlueprintPureNode with / without execution pins; BlueprintPure must not mutate state
BlueprintNativeEventBlueprint may override; the C++ default body is Name_Implementation
BlueprintImplementableEventBlueprint implements; no C++ body at all
BlueprintAuthorityOnly / BlueprintCosmeticBlueprint calls run only with network authority / never on a dedicated server (direct C++ calls are not filtered)
Server, Client, NetMulticast with Reliable or Unreliable, WithValidationRPCs — rules and examples in ue-networking-replication
ExecConsole command; the console routes to PlayerInput, PlayerController, Pawn, HUD, GameMode, CheatManager, GameState, PlayerCameraManager and GameInstance
CallInEditorAdds a button to the Details panel
cpp
// MyCharacter.cpp — the only body a BlueprintNativeEvent gets in C++
void AMyCharacter::OnDamageTaken_Implementation(float Amount) { CurrentHealth = FMath::Max(0.f, CurrentHealth - Amount); }

Function libraries, latent and async nodes, WorldContext/DefaultToSelf/ExpandEnumAsExecs and the rest of the Blueprint-facing meta= keys: ue-blueprint-cpp-interop.

USTRUCT, UENUM, UINTERFACE

cpp
// MyTypes.h
#pragma once
#include "CoreMinimal.h"
#include "MyTypes.generated.h"

USTRUCT(BlueprintType)
struct MYGAME_API FMyWeaponStats
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Weapon")
    float BaseDamage = 10.f;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Weapon")
    float FireRate = 0.5f;
};

UENUM(BlueprintType)
enum class EMyWeaponState : uint8      // Blueprint-visible enums are enum class : uint8
{
    Idle,
    Firing    UMETA(DisplayName="Firing Weapon"),
    MAX       UMETA(Hidden)
};
  • A UDataTable row type is a USTRUCT deriving from FTableRowBase (Engine/DataTable.h); see ue-data-assets-tables.
  • Initialize every USTRUCT member in-class; reflected structs are value types copied freely by the engine.
  • A polymorphic struct value (any USTRUCT derived from a base) is stored as FInstancedStruct or typed TInstancedStruct<FMyBase> (StructUtils/InstancedStruct.h, module CoreUObject).
  • UINTERFACE pairs a UMyThing : UInterface stub with an IMyThing class; specifier table and a native example are in references/property-specifiers.md. Blueprint-implementable interfaces belong to ue-blueprint-cpp-interop.

Containers

Full API, iteration and performance notes: references/container-patterns.md. Containers of UObject pointers are GC-tracked only as UPROPERTY() TArray<TObjectPtr<T>> (likewise TMap/TSet).

cpp
TArray<FString> Names;
Names.Add(TEXT("Alpha"));
Names.Emplace(TEXT("Beta"));                       // construct in place
Names.Reserve(100);                                // pre-size before a known number of Adds
int32 Idx = Names.Find(TEXT("Beta"));              // INDEX_NONE if absent
FString* Ptr = Names.FindByPredicate([](const FString& S) { return S.StartsWith(TEXT("A")); });
Names.Sort([](const FString& A, const FString& B) { return A.Len() < B.Len(); });
Names.Remove(TEXT("Alpha"));                       // order-preserving, O(n)
Names.RemoveAtSwap(0);                             // O(1), reorders
for (int32 i = Names.Num() - 1; i >= 0; --i) { if (Names[i].IsEmpty()) { Names.RemoveAt(i); } }  // never mutate inside ranged-for

TMap<FName, int32> ItemCounts;
int32& Count = ItemCounts.FindOrAdd(FName("Sword"));  // inserts default if absent
int32* Found = ItemCounts.Find(FName("Axe"));         // nullptr if absent
for (const TPair<FName, int32>& Pair : ItemCounts) { UE_LOG(LogMyGame, Log, TEXT("%s x%d"), *Pair.Key.ToString(), Pair.Value); }

TSet<FName> Tags;
Tags.Add(FName("Flying"));
bool bFlying = Tags.Contains(FName("Flying"));     // hashed, no duplicates

TOptional<float> MaybeHP;
float Safe = MaybeHP.Get(0.f);                     // default when unset

TVariant

cpp
#include "Misc/TVariant.h"
#include <type_traits>

TVariant<int32, float, FString> Value;             // first type must be default-constructible (or use FEmptyVariantState)
Value.Set<FString>(TEXT("Hello"));
if (const FString* Str = Value.TryGet<FString>()) { UE_LOG(LogMyGame, Log, TEXT("%s"), **Str); }

Visit([](auto& Held)                               // one generic lambda; branch with if constexpr
{
    using HeldType = std::decay_t<decltype(Held)>;
    if constexpr (std::is_same_v<HeldType, FString>) { UE_LOG(LogMyGame, Log, TEXT("%s"), *Held); }
}, Value);

Delegates

All declaration macros, binding methods and payload rules: references/delegate-patterns.md.

Macro familyBindingsBlueprintUse for
DECLARE_DELEGATE (_RetVal, _OneParam …)1NoSingle-owner callbacks, may return a value
DECLARE_MULTICAST_DELEGATENNoC++ events (DECLARE_TS_… for other threads)
DECLARE_DYNAMIC_DELEGATE1YesBlueprint-assignable callback (UFUNCTION targets only)
DECLARE_DYNAMIC_MULTICAST_DELEGATENYesUPROPERTY(BlueprintAssignable) events
cpp
// MyHealthComponent.h — delegate types are declared at file scope, above the UCLASS
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnMyHealthChangedNative, float /*Current*/, float /*Max*/);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnMyHealthChanged, float, CurrentHealth, float, MaxHealth);

// in UMyHealthComponent : public UActorComponent
UPROPERTY(BlueprintAssignable, Category="Health")
FOnMyHealthChanged OnHealthChanged;               // dynamic: needs UPROPERTY, params need names
FOnMyHealthChangedNative OnHealthChangedNative;   // native: no reflection, cheaper to broadcast

// in AMyListenerActor : public AActor
UFUNCTION()                                       // AddDynamic targets must be UFUNCTIONs
void HandleHealthChanged(float CurrentHealth, float MaxHealth);
void HandleHealthChangedNative(float Current, float Max);
UPROPERTY() TObjectPtr<UMyHealthComponent> Health;
FDelegateHandle NativeHandle;
cpp
// AMyListenerActor.cpp — bind in BeginPlay (after Super::), unbind in EndPlay (before Super::)
Health->OnHealthChanged.AddDynamic(this, &AMyListenerActor::HandleHealthChanged);
NativeHandle = Health->OnHealthChangedNative.AddUObject(this, &AMyListenerActor::HandleHealthChangedNative);
// EndPlay: guard with IsValid(Health) first
Health->OnHealthChanged.RemoveDynamic(this, &AMyListenerActor::HandleHealthChanged);
Health->OnHealthChangedNative.Remove(NativeHandle);

The owner broadcasts with OnHealthChanged.Broadcast(CurrentHealth, MaxHealth);. Both classes in full: references/delegate-patterns.md.

Single delegates bind with BindUObject, BindSP, BindRaw, BindStatic, BindLambda, BindWeakLambda(this, ...) and fire with ExecuteIfBound(...). For a lambda that captures this, prefer AddWeakLambda/BindWeakLambda so the binding dies with the object.

String Types

TypeUse forCompareNotes
FNameIdentifiers, asset and bone names, tagsO(1) integer, case-insensitiveInterned in a global table; immutable
FStringPaths, parsing, runtime-built textO(n)Mutable heap string; *Str yields const TCHAR*
FTextAnything a player reads—Localizable; build with LOCTEXT/NSLOCTEXT/FText::Format
cpp
float Health = 42.f; int32 Current = 3; int32 Max = 5;

FName   Tag(TEXT("Weapon.Rifle"));
FString S    = Tag.ToString();
FName   Back = FName(*S);
FString Msg  = FString::Printf(TEXT("HP: %.1f (%d/%d)"), Health, Current, Max);
int32 Parsed = FCString::Atoi(TEXT("42"));

#define LOCTEXT_NAMESPACE "MyGame"
FText Label = LOCTEXT("RifleName", "Assault Rifle");
FText Fmt   = FText::Format(LOCTEXT("HPFmt", "HP: {0}/{1}"), FText::AsNumber(Current), FText::AsNumber(Max));
#undef LOCTEXT_NAMESPACE
FText Other = NSLOCTEXT("MyGame", "Key", "Text");   // no #define needed
FText Raw   = FText::FromString(S);                 // not localized; runtime-generated text only

Object Lifetime and Pointers

The garbage collector keeps alive every UObject reachable from a root through reflected (UPROPERTY) references, FGCObject referencers, TStrongObjectPtr and rooted objects. A pointer the GC cannot see may dangle.

cpp
// Reflected members: TObjectPtr — GC-tracked, access-tracked in the editor, used like a raw pointer
UPROPERTY() TObjectPtr<UStaticMeshComponent> MeshComp;

// Legacy APIs that want raw pointers
void FillObjects(TArray<UObject*>& Out);
TArray<TObjectPtr<UObject>> Objects;
FillObjects(MutableView(Objects));                       // temporary TArray<UObject*>& view; ToRawPtr(Ptr) for a single read-only copy

// Non-owning reference: TWeakObjectPtr becomes null once the object is destroyed or collected
TWeakObjectPtr<AMyCharacter> WeakTarget;
if (AMyCharacter* Target = WeakTarget.Get()) { Target->OnDamageTaken(5.f); }

// Keep-alive from non-reflected code: scoped root instead of AddToRoot()/RemoveFromRoot()
TStrongObjectPtr<UMyDataObject> Pinned(NewObject<UMyDataObject>());

// Plain C++ types only: never wrap a UObject in TSharedPtr/TUniquePtr
struct FMyData { int32 Score = 0; };
TSharedPtr<FMyData> Data = MakeShared<FMyData>();
TWeakPtr<FMyData>   WeakData = Data;                     // Pin() returns a TSharedPtr if still alive

Non-UObject classes that hold UObjects derive from FGCObject and report them through TObjectPtr members; the raw UObject* overloads of AddReferencedObject(s) are deprecated and unsafe with incremental GC.

cpp
#include "UObject/GCObject.h"

class FMyManager : public FGCObject
{
public:
    virtual void AddReferencedObjects(FReferenceCollector& Collector) override { Collector.AddReferencedObject(ManagedObject); }
    virtual FString GetReferencerName() const override { return TEXT("FMyManager"); }
private:
    TObjectPtr<UMyDataObject> ManagedObject;   // TObjectPtr<T>& overload; raw UObject* is deprecated
};

CDO Access and Non-Null Parameters

cpp
#include "Misc/NotNull.h"

const UMyDataObject* Defaults = GetDefault<UMyDataObject>();        // class default object, read-only
UMyDataObject*       Settings = GetMutableDefault<UMyDataObject>(); // settings-style objects
UClass* SomeClass = AMyCharacter::StaticClass();
const AMyCharacter* ClassCDO = SomeClass->GetDefaultObject<AMyCharacter>();  // CDO of a runtime UClass

// TNotNull<T*>: caller cannot pass null; checked at the call site in Debug/Development, a plain pointer in Test and Shipping
void RegisterOwner(TNotNull<AActor*> Owner);
void RegisterOwner(TNotNull<AActor*> Owner)
{
    Owner->SetActorHiddenInGame(false);   // no null check needed inside
}

UClass::ClassDefaultObject is deprecated for direct access. TNotNull<T*> converts implicitly to T*; keep T* where null is a meaningful value.

Subsystems

Auto-instanced singletons owned by an engine object; no manual rooting. Override Initialize(FSubsystemCollectionBase&)/Deinitialize(); call Collection.InitializeDependency<UMyOtherSubsystem>() inside Initialize when ordering matters; return false from ShouldCreateSubsystem(UObject* Outer) const to opt out.

ClassHeaderOwner / lifetimeAccessor
UEngineSubsystemSubsystems/EngineSubsystem.hUEngine; whole processGEngine->GetEngineSubsystem<T>()
UGameInstanceSubsystemSubsystems/GameInstanceSubsystem.hUGameInstance; survives map changesGetGameInstance()->GetSubsystem<T>()
UWorldSubsystemSubsystems/WorldSubsystem.hUWorld; recreated per world; filter with DoesSupportWorldTypeGetWorld()->GetSubsystem<T>()
UTickableWorldSubsystemSubsystems/WorldSubsystem.hAs above, plus Tick(); must implement GetStatId()GetWorld()->GetSubsystem<T>()
ULocalPlayerSubsystemSubsystems/LocalPlayerSubsystem.hULocalPlayer; one per local playerLocalPlayer->GetSubsystem<T>(), ULocalPlayer::GetSubsystemFromController<T>(PC)
UEditorSubsystemEditorSubsystem.h (module EditorSubsystem)UEditorEngine; editor session onlyGEditor->GetEditorSubsystem<T>()
cpp
// MyInventorySubsystem.h
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MyInventorySubsystem.generated.h"

UCLASS()
class MYGAME_API UMyInventorySubsystem : public UGameInstanceSubsystem
{
    GENERATED_BODY()
public:
    virtual void Initialize(FSubsystemCollectionBase& Collection) override;
    virtual void Deinitialize() override;

    UFUNCTION(BlueprintCallable, Category="Inventory")
    void AddItem(FName ItemID, int32 Count);
private:
    TMap<FName, int32> Inventory;
};

// A ticking world subsystem adds these; Initialize/Deinitialize must call Super:: (the base registers the ticker)
UCLASS()
class MYGAME_API UMyTickableSubsystem : public UTickableWorldSubsystem
{
    GENERATED_BODY()
public:
    virtual void Tick(float DeltaTime) override;
    virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(UMyTickableSubsystem, STATGROUP_Tickables); }
    virtual bool DoesSupportWorldType(const EWorldType::Type WorldType) const override;  // e.g. Game and PIE only
};
cpp
// Access from an AActor member function
UMyInventorySubsystem* Inv = GetGameInstance()->GetSubsystem<UMyInventorySubsystem>();
UMyTickableSubsystem*  Tck = GetWorld()->GetSubsystem<UMyTickableSubsystem>();
APlayerController*     PC  = GetWorld()->GetFirstPlayerController();
UMyUISubsystem*        UI  = ULocalPlayer::GetSubsystemFromController<UMyUISubsystem>(PC);   // null-safe
UMyEngineSubsystem*    Eng = GEngine->GetEngineSubsystem<UMyEngineSubsystem>();

UWorldSubsystem also offers PostInitialize(), OnWorldBeginPlay(UWorld& InWorld), OnWorldEndPlay(UWorld& InWorld) and OnWorldComponentsUpdated(UWorld& World) overrides; ULocalPlayerSubsystem::GetLocalPlayer<T>() and UGameInstanceSubsystem::GetGameInstance() return the owner.

Replicated Properties

Both the specifier and the GetLifetimeReplicatedProps entry are required. Conditions (COND_*), RPC rules, push model and Iris: ue-networking-replication.

cpp
// MyReplicatedActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyReplicatedActor.generated.h"

UCLASS()
class MYGAME_API AMyReplicatedActor : public AActor
{
    GENERATED_BODY()
public:
    AMyReplicatedActor() { bReplicates = true; }
    virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
protected:
    UPROPERTY(ReplicatedUsing=OnRep_Health)
    float Health = 100.f;

    UFUNCTION()
    void OnRep_Health();                 // runs on clients after Health changes
};
cpp
// MyReplicatedActor.cpp
#include "MyReplicatedActor.h"
#include "Net/UnrealNetwork.h"          // required for DOREPLIFETIME

void AMyReplicatedActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(AMyReplicatedActor, Health);
}

void AMyReplicatedActor::OnRep_Health() {}

Logging

cpp
DECLARE_LOG_CATEGORY_EXTERN(LogMyGame, Log, All);   // MyGame.h (module header)
DEFINE_LOG_CATEGORY(LogMyGame);                     // MyGame.cpp
UE_LOG(LogMyGame, Warning, TEXT("HP low: %.1f"), 12.5f);

Verbosities (Logging/LogVerbosity.h): Fatal, Error, Warning, Display, Log, Verbose, VeryVerbose. Display prints to the console and the log file; Log goes to the log file only. Shipping builds compile UE_LOG out unless the target sets bUseLoggingInShipping = true. Categories, structured logging (UE_LOGFMT), runtime verbosity control and profiling: ue-testing-debugging.

Conditional Compilation Guards

cpp
#if WITH_EDITORONLY_DATA
    UPROPERTY(EditAnywhere, Category="Debug")
    bool bShowDebugSpheres = false;                 // reflected editor-only data
#endif
#if WITH_EDITOR
    virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;   // editor-only code
#endif
#if !UE_BUILD_SHIPPING
    void DrawDebugInfo();                           // Debug, Development and Test only
#endif

Deprecated — do not use

Do not emitUse in 5.8Source
SomeClass->ClassDefaultObjectGetDefault<T>(), GetMutableDefault<T>(), SomeClass->GetDefaultObject<T>()UE_DEPRECATED(5.6) in UObject/Class.h:4045
Collector.AddReferencedObject(RawUObjectPtr) with UObject* membersTObjectPtr<T> members passed to AddReferencedObject(s)UE_DEPRECATED(5.3) via UE_REFERENCE_COLLECTOR_REQUIRE_OBJECTPTR_DEPRECATED in UObject/UObjectGlobals.h:109
ToRawPtrArrayUnsafe(Array), ToRawPtrTArrayUnsafe(Array) on mutable arraysMutableView(Array) or keep TArray<TObjectPtr<T>>UE_DEPRECATED(5.6) in UObject/ObjectPtr.h:1112,1156
Visit(<overload-set helper struct>{...}, Variant)One generic lambda with if constexpr (std::is_same_v<...>)Helper is Lyra sample code; absent from 5.8 headers (Misc/TVariant.h)
GENERATED_USTRUCT_BODY(), GENERATED_UCLASS_BODY(), GENERATED_IINTERFACE_BODY()GENERATED_BODY()Legacy aliases in UObject/ObjectMacros.h:802-805
Obj->AddToRoot() / RemoveFromRoot() for temporary keep-aliveTStrongObjectPtr<T> (UObject/StrongObjectPtr.h)Rooting is permanent until removed; scoped pointer cannot leak
TMap::TIterator(Map, /*bRequiresRehashOnRemoval*/ true) (auto-Relax after removal)Map.CreateIterator(); call Compact()/Shrink() yourself if neededUE_DEPRECATED(5.8) in Containers/Map.h.inl:916; TSet::Relax() itself is not yet marked (Containers/SparseSet.h.inl:319)
TIsConst<T>, TIsMemberPointer<T>std::is_const_v<T>, std::is_member_pointer_v<T>UE_DEPRECATED(5.7) Templates/IsConst.h:15; UE_DEPRECATED(5.8) Templates/IsMemberPointer.h:14
FThreadSafeRefCountedObject, FRefCountBaseFRefCountedObject (now thread-safe)UE_DEPRECATED(5.8) in Templates/RefCounting.h:151-153
FCoreDelegates::OnPostEngineInit.AddRaw(...)FCoreDelegates::GetOnPostEngineInit().AddRaw(...)UE_DEPRECATED(5.8) in Misc/CoreDelegates.h:240
Delegate.ProcessMulticastDelegate<UObject>(Params)Delegate.ProcessDelegate<UObject>(Params)UE_DEPRECATED(5.8) in UObject/ScriptDelegates.h:1462
meta=(HideAssetPicker)meta=(HidePinAssetPicker)UE_DEPRECATED(5.6) in UObject/ObjectMacros.h:1673
ObjPtr.IsRemote()ObjPtr.GetResidence()UE_DEPRECATED(5.8) in UObject/ObjectPtr.h:206

Common Mistakes

Raw UObject* member without UPROPERTY — dangling after GC:

cpp
UMyDataObject* Obj;                        // WRONG: invisible to GC
UPROPERTY() TObjectPtr<UMyDataObject> Obj; // RIGHT

Mutating a TArray inside ranged-for — iterator invalidation:

cpp
TArray<AActor*> Actors;
for (AActor* A : Actors) { Actors.Remove(A); }                                                      // WRONG
for (int32 i = Actors.Num() - 1; i >= 0; --i) { if (!IsValid(Actors[i])) { Actors.RemoveAt(i); } }  // RIGHT

TSharedPtr around a UObject — two owners, one crash: UObjects are owned by the GC; use TObjectPtr, TWeakObjectPtr or TStrongObjectPtr.

.generated.h not the last include, or MYGAME_API missing: UnrealHeaderTool rejects the header; the missing API macro surfaces later as LNK2019 from other modules.

AddDynamic bound to a non-UFUNCTION: the reflection lookup fails at runtime and the handler never runs; mark it UFUNCTION().

UPROPERTY/UFUNCTION written above a .cpp definition: UnrealHeaderTool only parses headers; the macro is silently ignored. Put it on the declaration inside the class body.

BlueprintNativeEvent without Name_Implementation: unresolved external at link time. Declare virtual void Name_Implementation(...) and define it.

UTickableWorldSubsystem::Initialize/Deinitialize override without Super::: the tickable registration lives in the base implementation; the subsystem never ticks.

Reading ClassDefaultObject directly: deprecated and about to become private; use GetDefault<T>().

Log verbosity "not showing up": Log is file-only by design; use Display for console output.

Related Skills

  • ue-actor-component-architecture — AActor/UActorComponent lifecycle, spawning, tick groups, component setup
  • ue-module-build-system — Build.cs, module dependencies, API export macros, include paths, PCH
  • ue-testing-debugging — log categories and verbosity, structured logging, automation tests, profiling
  • ue-networking-replication — DOREPLIFETIME conditions, RPC rules, push model, Iris
  • ue-blueprint-cpp-interop — UBlueprintFunctionLibrary, latent and async actions, Blueprint-implementable
  • Also relevant: ue-data-assets-tables, ue-animation-system, ue-async-threading, ue-audio-system, ue-editor-tools, ue-gameplay-abilities, ue-gameplay-framework, ue-gameplay-tags-messaging, ue-input-system, ue-mass-entity, ue-materials-rendering, ue-serialization-savegames, ue-state-trees, ue-ui-umg-slate, ue-world-level-streaming

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Ue Cpp Foundations AI skill do?

Use when writing Unreal Engine C++ that touches the reflection system or core types: UCLASS, UPROPERTY, UFUNCTION, USTRUCT, UENUM, GENERATED_BODY, TObjectPtr, TWeakObjectPtr, TSharedPtr, FGCObject, TArray, TMap, TSet, TOptional, TVariant, DECLARE_DYNAMIC_MULTICAST_DELEGATE, AddDynamic, FName, FString, FText, UGameInstanceSubsystem, UWorldSubsystem, GetDefault, TNotNull. Also use when the user mentions 'UE C++', 'garbage collection', 'CDO', 'smart pointers' or 'specifiers'. For Actor and component lifecycle, see ue-actor-component-architecture; for Build.cs and modules, see ue-module-build-s...

Why use Ue Cpp Foundations on TypingMind?

Because you install it once and use it with any model. Ue Cpp Foundations is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Ue Cpp Foundations in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/quodsoler/unreal-engine-skills/tree/main/skills/ue-cpp-foundations. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Ue Cpp Foundations?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Ue Cpp Foundations?

As many as you like. As long as a model supports skills, you can use Ue Cpp Foundations with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Ue Cpp Foundations AI skill free?

Yes. It is published on GitHub by quodsoler under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇