quodsoler/unreal-engine-skills4 files

Ue Cpp Foundations

Use when writing Unreal Engine C++ code involving UPROPERTY, UFUNCTION, UCLASS, TArray, TMap, delegates, FString, garbage collection, or smart pointers. Also use when the user asks about "UE C++", USTRUCT, UENUM, FName, FText, TObjectPtr, TWeakObjectPtr, UObject lifetime, UE_LOG, or UE subsystems. For module build configuration, see ue-module-build-system. For Actor/Component architecture, see ue-actor-component-architecture.

Specification
Skill ID
quodsoler/unreal-engine-skills/ue-cpp-foundations
Publisher
quodsoler
Repository
unreal-engine-skills
Installs
0
Files
4
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

quodsoler/unreal-engine-skills/ue-cpp-foundationsInstalls these files
  • SKILL.md
  • references/container-patterns.md
  • references/delegate-patterns.md
  • references/property-specifiers.md

What this skill tells the agent

UE C++ Foundations

You are an expert in Unreal Engine's C++ extensions and property system.

Context

Read .agents/ue-project-context.md for engine version, coding conventions, and project-specific rules. Engine version matters: UE5 uses TObjectPtr<> where UE4 used raw UObject*, and GENERATED_BODY() replaces GENERATED_USTRUCT_BODY() in structs.

Before You Start

Ask which area the user needs help with if unclear:

  • Macros & Reflection — UCLASS, UPROPERTY, UFUNCTION, USTRUCT, UENUM
  • Containers — TArray, TMap, TSet, TOptional
  • Delegates — static, dynamic, multicast, binding patterns
  • Strings — FName, FString, FText conversion and formatting
  • Memory & GC — TObjectPtr, TWeakObjectPtr, TSharedPtr, GC roots
  • Logging — UE_LOG, log categories, verbosity
  • Subsystems — GameInstance, World, LocalPlayer subsystems

UObject Macros & Reflection

All UE reflection macros require GENERATED_BODY() inside the class/struct and the corresponding .generated.h include.

UCLASS()

SpecifierEffect
BlueprintableBlueprint subclassing allowed
BlueprintTypeUsable as Blueprint variable
AbstractCannot be instantiated
NotBlueprintableBlocks Blueprint subclassing
Config=<Name>Loads UPROPERTY(Config) from <Name>.ini
TransientNot saved/serialized
Within=<OuterClass>Outer must be of given type
UCLASS(Blueprintable, BlueprintType)
class MYGAME_API UMyDataObject : public UObject
{
    GENERATED_BODY()
public:
    UMyDataObject();
};

Full specifier list: references/property-specifiers.md.

UPROPERTY()

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

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

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Config")
    int32 MaxLevel = 50;

    UPROPERTY(ReplicatedUsing=OnRep_Health, Category="Replication")
    float ReplicatedHealth;

    UPROPERTY(Transient)                             // Not serialized; GC still tracks
    TObjectPtr<UParticleSystemComponent> CachedFX;

    UPROPERTY(SaveGame, BlueprintReadWrite, Category="Persistence")
    int32 PlayerScore;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stats",
              meta=(ClampMin="0.0", ClampMax="1.0"))
    float DamageMultiplier = 1.f;

    UFUNCTION()
    void OnRep_Health();

    virtual void GetLifetimeReplicatedProps(
        TArray<FLifetimeProperty>& OutLifetimeProps) const override;
};

UFUNCTION()

UFUNCTION(BlueprintCallable, Category="Actions")
void PerformAttack(float Damage);

UFUNCTION(BlueprintPure, Category="Queries")
float GetHealthPercent() const;

UFUNCTION(BlueprintNativeEvent, Category="Events")  // C++ provides _Implementation
void OnDamageTaken(float Amount);
virtual void OnDamageTaken_Implementation(float Amount);

UFUNCTION(BlueprintImplementableEvent, Category="Events")  // Blueprint must implement
void OnLevelUp(int32 NewLevel);

UFUNCTION(Server, Reliable, WithValidation)         // RPC: runs on server
void ServerFireWeapon(FVector Origin, FVector Direction);
void ServerFireWeapon_Implementation(FVector Origin, FVector Direction);
bool ServerFireWeapon_Validate(FVector Origin, FVector Direction);

UFUNCTION(Client, Reliable)                        // RPC: runs on owning client
void ClientShowDamageNumber(float Amount);
void ClientShowDamageNumber_Implementation(float Amount);

UFUNCTION(NetMulticast, Reliable)                   // RPC: runs on all
void MulticastPlayEffect(FVector Location);
void MulticastPlayEffect_Implementation(FVector Location);

UFUNCTION(Exec)                                    // Console command (~ in-game)
void DebugResetStats();                            // Works on PC, Pawn, HUD, GM, GI, CheatManager

USTRUCT() and UENUM()

// UE5: always GENERATED_BODY() — never GENERATED_USTRUCT_BODY()
USTRUCT(BlueprintType)
struct MYGAME_API FWeaponStats
{
    GENERATED_BODY()
    UPROPERTY(EditAnywhere, BlueprintReadWrite) float BaseDamage = 10.f;
    UPROPERTY(EditAnywhere, BlueprintReadWrite) float FireRate   = 0.5f;
};

// DataTable row
USTRUCT(BlueprintType)
struct MYGAME_API FEnemyTableRow : public FTableRowBase
{
    GENERATED_BODY()
    UPROPERTY(EditAnywhere, BlueprintReadWrite) FName EnemyID;
    UPROPERTY(EditAnywhere, BlueprintReadWrite) TSoftClassPtr<AActor> SpawnClass;
};

UENUM(BlueprintType)
enum class EWeaponState : uint8
{
    Idle      UMETA(DisplayName="Idle"),
    Firing    UMETA(DisplayName="Firing"),
    Reloading UMETA(DisplayName="Reloading"),
};

UE Containers

See references/container-patterns.md for full API and performance guide.

TArray — Ordered Dynamic Array

TArray<FString> Names;
Names.Add(TEXT("Alpha"));
Names.Emplace(TEXT("Beta"));       // Construct in-place (avoids copy)
Names.Reserve(100);                // Pre-allocate

FString First = Names[0];
bool bHas     = Names.Contains(TEXT("Alpha"));
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);             // Fast O(1), destroys order

for (const FString& N : Names) { /* do NOT add/remove during ranged-for */ }
for (int32 i = Names.Num()-1; i >= 0; --i) { if (Names[i].IsEmpty()) Names.RemoveAt(i); }

TMap — Hash Map

TMap<FName, int32> ItemCounts;
ItemCounts.Add(FName("Sword"), 3);

int32& Ref  = ItemCounts.FindOrAdd(FName("Sword")); // Insert default if absent
int32* Ptr  = ItemCounts.Find(FName("Axe"));        // nullptr if absent
bool   bHas = ItemCounts.Contains(FName("Shield"));
ItemCounts.Remove(FName("Shield"));

for (const TPair<FName, int32>& Pair : ItemCounts) { /* ... */ }

TSet — Hash Set

TSet<FName> Tags;
Tags.Add(FName("Flying"));
bool bFlying          = Tags.Contains(FName("Flying"));
TSet<FName> Intersect = Tags.Intersect(OtherTags);
TSet<FName> Union     = Tags.Union(OtherTags);

TOptional

TOptional<float> MaybeHP;
if (MaybeHP.IsSet()) { float H = MaybeHP.GetValue(); }
float Safe = MaybeHP.Get(0.f);  // Default if not set
MaybeHP = 75.f;
MaybeHP.Reset();

TVariant

// Type-safe tagged union — avoids unsafe casts
TVariant<int32, float, FString> Value;
Value.Set<FString>(TEXT("Hello"));

if (Value.IsType<FString>())
{
    const FString& Str = Value.Get<FString>();
}

// Visit — use explicit overloads; LexToString(V) fails when V is FString.
Visit(TOverloaded{
    [](int32  V) { UE_LOG(LogTemp, Log, TEXT("%d"), V); },
    [](float  V) { UE_LOG(LogTemp, Log, TEXT("%f"), V); },
    [](const FString& V) { UE_LOG(LogTemp, Log, TEXT("%s"), *V); },
}, Value);

Delegates

See references/delegate-patterns.md for all declaration macros and binding methods.

Choosing the Right Type

TypeBindingsBlueprintWhen to Use
DECLARE_DELEGATE1NoInternal single-owner callback
DECLARE_MULTICAST_DELEGATENNoInternal multi-listener events
DECLARE_DYNAMIC_DELEGATE1YesBlueprint-assignable single callback
DECLARE_DYNAMIC_MULTICAST_DELEGATENYesBlueprint-bindable events (most common)

Declaration, Binding, Invocation

// File scope (before UCLASS)
DECLARE_DELEGATE_OneParam(FOnItemPickedUp, AActor*);
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnHealthChanged, float, float);

Use this skill in a project

Open any RiverX project, search the Skills panel for quodsoler/unreal-engine-skills/ue-cpp-foundations, and install it in one click.

Use this skill