Essential C++ for Unreal Engine Development

The C++ Knowledge Map for Unreal

Think of Unreal Engine as a massive spaceship. You don't need to understand every circuit to fly it, but you do need to know the essential controls. This guide shows you exactly which C++ concepts are critical for Unreal Engine development.

Classes and Objects: The Foundation

Unreal Engine is built entirely on object-oriented programming. Every actor, component, and system is a class. You'll be creating and extending classes constantly.

graph TD A[C++ Class Concepts] --> B[Basic Classes] A --> C[Inheritance] A --> D[Polymorphism] B --> E[Data Members] B --> F[Member Functions] B --> G[Access Specifiers] C --> H[Single Inheritance] C --> I[Virtual Functions] C --> J[Abstract Classes] D --> K[Function Overriding] D --> L[Dynamic Dispatch] style A fill:#e74c3c style C fill:#f39c12 style D fill:#f39c12

What You Must Know

// 1. Basic Class Structure - CRITICAL for Unreal
class GameCharacter {
private:
    float health;          // Unreal uses float extensively
    FString name;          // Unreal's string type
    
protected:
    int32 level;           // Unreal's int32 typedef
    
public:
    // Constructor - Called when object is created
    GameCharacter() : health(100.0f), level(1) {
        // Initialization
    }
    
    // Destructor - Called when object is destroyed
    virtual ~GameCharacter() {
        // Cleanup - Unreal handles most cleanup automatically
    }
    
    // Getter/Setter pattern used everywhere in Unreal
    float GetHealth() const { return health; }
    void SetHealth(float newHealth) { health = newHealth; }
    
    // Virtual functions for polymorphism
    virtual void TakeDamage(float damage) {
        health -= damage;
    }
};

// 2. Inheritance - CRITICAL for extending Unreal classes
class PlayerCharacter : public GameCharacter {
private:
    int32 experience;
    
public:
    PlayerCharacter() : GameCharacter(), experience(0) {
        // Call parent constructor
    }
    
    // Override parent function
    virtual void TakeDamage(float damage) override {
        // Custom player damage logic
        float reducedDamage = damage * 0.8f;  // Players take less damage
        GameCharacter::TakeDamage(reducedDamage);
    }
    
    // New functions specific to player
    void GainExperience(int32 amount) {
        experience += amount;
        CheckLevelUp();
    }
    
private:
    void CheckLevelUp();
};

// 3. Abstract Classes/Interfaces - Used for Unreal interfaces
class IDamageable {
public:
    virtual ~IDamageable() = default;
    virtual void TakeDamage(float damage) = 0;  // Pure virtual
    virtual bool IsDead() const = 0;
};

Pointers and References: Memory Navigation

Unreal Engine uses pointers everywhere. You'll be constantly working with pointers to Actors, Components, and other game objects. Understanding pointers is non-negotiable.

Essential Pointer Knowledge

// 1. Basic Pointer Operations - MUST KNOW
void PointerBasics() {
    // Creating pointers
    AActor* ActorPtr = nullptr;  // Always initialize to nullptr
    
    // Getting pointers from Unreal
    ActorPtr = GetWorld()->SpawnActor(ActorClass);
    
    // Checking validity - CRITICAL IN UNREAL
    if (ActorPtr != nullptr) {
        // Safe to use
        ActorPtr->SetActorLocation(FVector(0, 0, 100));
    }
    
    // Better validity check
    if (IsValid(ActorPtr)) {
        // Checks both nullptr and pending kill
        ActorPtr->Destroy();
    }
}

// 2. References - Used for function parameters
void ProcessCharacter(const ACharacter& Character) {
    // Can't be null, always valid
    float Health = Character.GetHealth();
}

// 3. Pointer vs Reference Usage in Unreal
class UMyComponent : public UActorComponent {
private:
    // WRONG - Never store references as members
    // AActor& OwnerRef;  // This will cause problems!
    
    // CORRECT - Use pointers with UPROPERTY
    UPROPERTY()
    AActor* Owner;
    
    // CORRECT - Weak pointer for optional references
    TWeakObjectPtr Target;
    
public:
    // References are fine for parameters
    void Initialize(AActor& InOwner) {
        Owner = &InOwner;  // Convert to pointer for storage
    }
    
    // Const references for efficiency
    void ProcessData(const FVector& Location) {
        // No copy made, efficient
    }
};

// 4. Smart Pointers in Unreal (Different from std::)
void UnrealSmartPointers() {
    // TSharedPtr - Reference counted (not for UObjects!)
    TSharedPtr SharedData = MakeShareable(new FMyStruct());
    
    // TUniquePtr - Single owner (not for UObjects!)
    TUniquePtr RenderData = MakeUnique();
    
    // TWeakObjectPtr - For UObjects
    TWeakObjectPtr WeakActor = SomeActor;
    if (WeakActor.IsValid()) {
        WeakActor->DoSomething();
    }
}

Memory Management: Unreal's Garbage Collection

Unlike standard C++, Unreal Engine manages memory for you through garbage collection. Understanding how this works is crucial to avoid crashes and memory leaks.

graph TD A[Unreal Memory Management] --> B[UObject System] A --> C[Non-UObject Memory] B --> D[Automatic GC] B --> E[UPROPERTY Protection] B --> F[Reference Counting] C --> G[Manual Management] C --> H[Smart Pointers] C --> I[Custom Allocators] style B fill:#27ae60 style C fill:#e74c3c style D fill:#27ae60 style G fill:#e74c3c

Memory Rules You Must Follow

// 1. UObject Memory Management - CRITICAL
class MYPROJECT_API AMyActor : public AActor {
    GENERATED_BODY()
    
private:
    // CORRECT - UPROPERTY protects from GC
    UPROPERTY()
    UStaticMeshComponent* MeshComponent;
    
    // WRONG - Will be garbage collected!
    UStaticMeshComponent* UnprotectedComponent;
    
    // CORRECT - For non-UObjects
    TUniquePtr StructData;
    
public:
    AMyActor() {
        // CORRECT - Let Unreal manage UObject memory
        MeshComponent = CreateDefaultSubobject(TEXT("Mesh"));
        
        // WRONG - Never use new/delete with UObjects!
        // UnprotectedComponent = new UStaticMeshComponent();  // NEVER DO THIS!
        
        // CORRECT - For non-UObjects
        StructData = MakeUnique();
    }
    
    void SpawnExample() {
        // CORRECT - Spawn actors through the world
        AActor* NewActor = GetWorld()->SpawnActor(ActorClass);
        
        // WRONG - Never create actors with new
        // AActor* BadActor = new AActor();  // CRASH!
    }
    
    ~AMyActor() {
        // Don't delete UObject pointers!
        // MeshComponent will be handled by GC
        
        // StructData will be automatically deleted (unique_ptr)
    }
};

// 2. Preventing Garbage Collection
void PreventGC() {
    // Method 1: UPROPERTY
    UPROPERTY()
    AActor* ProtectedActor;
    
    // Method 2: Add to root (rare, special cases)
    UObject* ImportantObject = NewObject();
    ImportantObject->AddToRoot();
    // Remember to remove: ImportantObject->RemoveFromRoot();
    
    // Method 3: Strong references in containers
    UPROPERTY()
    TArray ActorArray;  // All actors in array are protected
}

// 3. Working with Non-UObject Memory
struct FGameData {
    TArray Values;
    FString Name;
    
    // Unreal containers manage their own memory
    void AddValue(float Val) {
        Values.Add(Val);  // Automatic resize
    }
};

// 4. Memory Best Practices
class UMyGameSystem : public UObject {
private:
    // Use Unreal's containers
    TArray Scores;           // Dynamic array
    TMap PlayerStats;  // Hash map
    TSet TrackedActors;    // Hash set
    
public:
    void MemoryEfficientOperations() {
        // Pre-allocate for performance
        Scores.Reserve(100);
        
        // Move semantics work in Unreal
        TArray TempArray;
        Scores = MoveTemp(TempArray);
        
        // Shrink when done
        Scores.Shrink();
    }
};

Templates: Understanding Unreal's Generic Code

While you don't need to be a template wizard, you must understand basic templates because Unreal uses them extensively in containers and smart pointers.

Template Essentials for Unreal

// 1. Using Unreal's Template Containers - MUST KNOW
void ContainerBasics() {
    // TArray - Dynamic array (like std::vector)
    TArray Enemies;
    Enemies.Add(Enemy1);
    Enemies.Remove(Enemy2);
    
    for (AActor* Enemy : Enemies) {
        // Process each enemy
    }
    
    // TMap - Hash map (like std::unordered_map)
    TMap PlayerScores;
    PlayerScores.Add("Player1", 100);
    int32* Score = PlayerScores.Find("Player1");
    
    // TSet - Hash set
    TSet UniqueNames;
    UniqueNames.Add("ItemName");
}

// 2. Template Functions You'll Use - MUST KNOW
void TemplateFunctions() {
    AActor* Actor = GetSomeActor();
    
    // Cast - Safe casting with nullptr on failure
    ACharacter* Character = Cast(Actor);
    if (Character) {
        // Successfully cast to ACharacter
    }
    
    // GetComponent - Get component by type
    UHealthComponent* Health = Actor->GetComponent();
    
    // SpawnActor - Spawn actors of specific type
    AProjectile* Projectile = GetWorld()->SpawnActor(
        ProjectileClass, Location, Rotation
    );
}

// 3. TSubclassOf - Class type safety - IMPORTANT
class AWeaponPickup : public AActor {
    // This ensures WeaponClass is a subclass of AWeapon
    UPROPERTY(EditDefaultsOnly)
    TSubclassOf WeaponClass;
    
    void SpawnWeapon() {
        if (WeaponClass) {
            GetWorld()->SpawnActor(WeaponClass);
        }
    }
};

// 4. Creating Simple Templates - NICE TO KNOW
template
T* FindActorOfType(UWorld* World) {
    for (TActorIterator It(World); It; ++It) {
        return *It;  // Return first found
    }
    return nullptr;
}

// Usage
APlayerController* PC = FindActorOfType(GetWorld());

Function Pointers and Delegates

Unreal uses delegates extensively for event handling. They're like function pointers on steroids, allowing flexible event systems.

graph TD A[Unreal Event System] --> B[Delegates] A --> C[Events] A --> D[Callbacks] B --> E[Single Delegate] B --> F[Multicast Delegate] B --> G[Dynamic Delegate] G --> H[Blueprint Compatible] F --> I[Multiple Listeners] style A fill:#e74c3c style B fill:#f39c12 style G fill:#27ae60

Delegates and Lambdas

// 1. Basic Delegates - IMPORTANT for Unreal
// Declare delegate types
DECLARE_DELEGATE_OneParam(FOnHealthChanged, float);
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnDamageDealt, AActor*, float);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPlayerDied, ACharacter*, Character);

class AGameCharacter : public ACharacter {
public:
    // Single delegate
    FOnHealthChanged OnHealthChanged;
    
    // Multicast - multiple listeners
    FOnDamageDealt OnDamageDealt;
    
    // Dynamic - Blueprint compatible
    UPROPERTY(BlueprintAssignable)
    FOnPlayerDied OnPlayerDied;
    
    void TakeDamage(float Damage) {
        Health -= Damage;
        
        // Execute delegates
        OnHealthChanged.ExecuteIfBound(Health);
        OnDamageDealt.Broadcast(this, Damage);
        
        if (Health <= 0) {
            OnPlayerDied.Broadcast(this);
        }
    }
};

// 2. Using Lambdas - VERY COMMON in Unreal
void LambdaExamples() {
    // Timer with lambda
    FTimerHandle TimerHandle;
    GetWorld()->GetTimerManager().SetTimer(TimerHandle, [this]() {
        // This code runs after 2 seconds
        UE_LOG(LogTemp, Warning, TEXT("Timer fired!"));
    }, 2.0f, false);
    
    // Binding to delegates with lambdas
    Character->OnHealthChanged.BindLambda([](float NewHealth) {
        UE_LOG(LogTemp, Warning, TEXT("Health: %f"), NewHealth);
    });
    
    // Array operations with lambdas
    TArray Actors;
    Actors.Sort([](const AActor& A, const AActor& B) {
        return A.GetName() < B.GetName();
    });
    
    // Finding with predicates
    AActor** Found = Actors.FindByPredicate([](AActor* Actor) {
        return Actor->GetName() == "TargetActor";
    });
}

// 3. Function Binding - COMMON pattern
class UMyWidget : public UUserWidget {
    UFUNCTION()
    void OnButtonClicked() {
        // Handle click
    }
    
    void SetupWidget() {
        if (MyButton) {
            // Bind member function to button click
            MyButton->OnClicked.AddDynamic(this, &UMyWidget::OnButtonClicked);
        }
    }
};

Macros and Preprocessor

Unreal Engine uses macros extensively. You don't need to create complex macros, but you must understand how to use Unreal's existing ones.

Macro Usage Guide

// 1. Essential Class Declaration Macros - MUST KNOW
UCLASS()  // Makes class visible to Unreal
class MYPROJECT_API AMyActor : public AActor {
    GENERATED_BODY()  // Required boilerplate
    
    // UPROPERTY macros for reflection
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
    float Health = 100.0f;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    UStaticMeshComponent* MeshComponent;
    
    // UFUNCTION macros for Blueprint/reflection
    UFUNCTION(BlueprintCallable, Category = "Actions")
    void PerformAction();
    
    UFUNCTION(BlueprintImplementableEvent, Category = "Events")
    void OnActionCompleted();  // Implemented in Blueprint
};

// 2. Logging Macros - ESSENTIAL for debugging
void LoggingExamples() {
    // Basic logging
    UE_LOG(LogTemp, Warning, TEXT("Simple message"));
    
    // With variables
    int32 Score = 100;
    FString PlayerName = "John";
    UE_LOG(LogTemp, Display, TEXT("Player %s scored %d"), *PlayerName, Score);
    
    // Different log levels
    UE_LOG(LogTemp, Display, TEXT("Info message"));     // Gray
    UE_LOG(LogTemp, Warning, TEXT("Warning message"));  // Yellow
    UE_LOG(LogTemp, Error, TEXT("Error message"));      // Red
}

// 3. Assertion Macros - IMPORTANT for debugging
void AssertionExamples() {
    // check - Halts execution in non-shipping builds
    check(Actor != nullptr);
    check(Health > 0);
    
    // ensure - Logs error but continues
    ensure(Component != nullptr);
    
    // verify - Like check but evaluates in all builds
    verify(ProcessData());
}

// 4. Platform Macros - USEFUL
void PlatformSpecific() {
    #if PLATFORM_WINDOWS
        // Windows-specific code
    #elif PLATFORM_MAC
        // Mac-specific code
    #elif PLATFORM_LINUX
        // Linux-specific code
    #endif
    
    #if WITH_EDITOR
        // Editor-only code
    #endif
}

STL vs Unreal Containers

Unreal has its own container types. While STL knowledge helps, you'll use Unreal's containers exclusively in engine code.

// Container Comparison Reference
// STL                    → Unreal Equivalent
// std::vector           → TArray
// std::unordered_map    → TMap
// std::unordered_set    → TSet
// std::string           → FString
// std::shared_ptr       → TSharedPtr (not for UObjects!)
// std::unique_ptr       → TUniquePtr (not for UObjects!)
// std::function         → TFunction / TDelegate

// Unreal Container Examples - MUST KNOW
void UnrealContainers() {
    // TArray - Dynamic array
    TArray Numbers;
    Numbers.Add(42);
    Numbers.AddUnique(42);  // Won't add duplicate
    Numbers.Remove(42);
    Numbers.Empty();  // Clear array
    
    // Useful TArray operations
    Numbers.Reserve(100);  // Pre-allocate
    Numbers.Sort();
    Numbers.Reverse();
    int32 Index = Numbers.Find(42);
    bool bContains = Numbers.Contains(42);
    
    // TMap - Key-value pairs
    TMap ActorMap;
    ActorMap.Add("Player", PlayerActor);
    AActor** FoundActor = ActorMap.Find("Player");
    ActorMap.Remove("Player");
    
    // Iterating TMap
    for (const auto& Pair : ActorMap) {
        FString Key = Pair.Key;
        AActor* Value = Pair.Value;
    }
    
    // TSet - Unique elements
    TSet Tags;
    Tags.Add("Enemy");
    bool bHasTag = Tags.Contains("Enemy");
}

// String Types - CRITICAL
void UnrealStrings() {
    // FString - Mutable string
    FString MyString = TEXT("Hello");
    MyString += TEXT(" World");
    
    // FName - Immutable, case-insensitive identifier
    FName TagName("EnemyTag");
    
    // FText - Localized display text
    FText DisplayText = FText::FromString("Display This");
    
    // Conversions
    FString StringFromName = TagName.ToString();
    FName NameFromString(*MyString);
    const TCHAR* CString = *MyString;  // Get C-string
}

Error Handling: Unreal Style

Unreal doesn't use exceptions. Instead, it uses return codes, validity checks, and assertions.

// 1. No Exceptions! - CRITICAL
// WRONG - Unreal doesn't use exceptions
void WrongWay() {
    try {
        // Don't do this in Unreal!
        throw std::exception("Error");
    } catch (...) {
        // Unreal is compiled with exceptions disabled
    }
}

// CORRECT - Unreal error handling
bool CorrectWay(AActor*& OutActor) {
    OutActor = FindActor();
    if (!IsValid(OutActor)) {
        UE_LOG(LogTemp, Error, TEXT("Failed to find actor"));
        return false;
    }
    return true;
}

// 2. Validity Checking - ESSENTIAL
void ValidityChecks() {
    // Basic null check
    if (Actor != nullptr) {
        // Safe to use
    }
    
    // Better - checks null and pending kill
    if (IsValid(Actor)) {
        // Actor is truly valid
    }
    
    // For weak pointers
    if (WeakActor.IsValid()) {
        AActor* Actor = WeakActor.Get();
    }
}

// 3. Assertions for Development - IMPORTANT
void DevelopmentAssertions() {
    // check - Only in non-shipping builds
    check(Component != nullptr);
    checkf(Health > 0, TEXT("Health was %f"), Health);
    
    // ensure - Logs and continues
    if (!ensure(Data != nullptr)) {
        return;  // Handle gracefully
    }
    
    // Conditional checks
    checkNoEntry();  // Should never reach this code
    checkNoReentry();  // Prevent recursive calls
}

Quick Reference: C++ to Unreal

graph TD A[Start Unreal C++] --> B{Know Classes?} B -->|No| C[Learn OOP First] B -->|Yes| D{Know Pointers?} D -->|No| E[Learn Pointers] D -->|Yes| F{Know Templates Basics?} F -->|No| G[Learn Container Usage] F -->|Yes| H[Ready for Unreal!] C --> D E --> F G --> H style H fill:#27ae60 style C fill:#e74c3c style E fill:#e74c3c style G fill:#f39c12

Pre-Flight Checklist

Before starting with Unreal Engine, make sure you can:

Remember: You don't need to be a C++ expert to start with Unreal. Focus on the fundamentals, and learn advanced features as you need them. Unreal's systems will guide you toward good practices!

Practice Exercise: Unreal-Ready Code

Challenge: Convert Standard C++ to Unreal Style

Take this standard C++ code and convert it to Unreal Engine style:

// Standard C++ Version
#include 
#include 
#include 

class GameObject {
private:
    std::string name;
    float health;
    std::vector children;
    
public:
    GameObject(const std::string& n) : name(n), health(100.0f) {}
    
    void TakeDamage(float damage) {
        health -= damage;
        if (health <= 0) {
            delete this;  // Dangerous!
        }
    }
    
    void AddChild(GameObject* child) {
        children.push_back(child);
    }
};

// Your Task: Convert to Unreal style
// Hints:
// - Inherit from AActor
// - Use UCLASS() and GENERATED_BODY()
// - Replace std::string with FString
// - Replace std::vector with TArray
// - Add UPROPERTY macros
// - Remove manual delete
// - Add proper null checks