Architecture

Source Code

The Unreal types have prefixes to indicate their kind:

ℹ️
The source file and header file for a type do not include the prefix.

Some variables also have prefixes:

Containers

Unreal containers are templated classes.

TypeContainer
VectorTArray
ListTList
MapTMap
Unordered MapTMultiMap
SetTSet

TArray supports the standard allocators (FHeapAllocator and TInlineAllocator).

TMap, TMultiMap, and TSet to do not support the standard allocators. They support the TSetAllocator, which defines how many hash buckets the container should use.

TArray

TArray is a dynamic array of contiguous values (similar to std::vector).

TArray supports garbage collection when declared with the UPROPERTY specifier and contain values derived from UObject.

TArray uses by default, the heap-based allocator.

⚠️
When using a non-default allocator, the UPROPERTY specifier is not supported.

When a TArray is declared, no memory is allocated.

Insertions

A TArray can be populated using different methods:

Deletions

Access

The elements in the array are accessed using the subscript operator with a zero-based index. The operator can be used to mutate the elements inside the array.

Arrays can be iterated using the C++ ranged-for loops or the regular index-based loops.

Searching

Arrays can be searched for specific elements with:

Sorting

Arrays can be sorted with:

TMap

TMap is a collection of key-value pairs (similar to std::map).

TMap keys are unique.

When adding a new key-value pair with a key that matches an existing pair, the new pair will replace the old one.

The backing data structure of a TMap is a sparse array, which is an array that efficiently supports gaps between its elements.

⚠️
The elements in a TMap are not guaranteed to be ordered in memory.

To determine if a map contains a specific key, call the Contains function.

The values in a map are accessed using the subscript operator with a key. The operator can be used to mutate the elements inside the map.

The Remove function removes all the elements for the specified key. The function returns he number of elements that were removed.

⚠️
Removing elements can leave gaps in the backing data structure.

A map can be sorted:

TMultiMap

TMultiMap is a collection of key-value pairs (similar to std::unordered_map).

TMultiMap can store multiple, identical keys.

When adding a new key-value pair with a key that matches an existing pair, the old pair and the new pair will be stored.

TSet

TSet is a collection of unique values (similar to std::set).

Hashes

The TMap, TMultiMap and TSet types use hash values.

The hash function takes a const pointer or reference to a type and returns a uint32 hash value.

⚠️
Two equal objects should always return the same hash code.

Numeric Types

TypesKind
int8/uint88-bit signed/unsigned integer
int16/uint1616-bit signed/unsigned integer
int32/uint3232-bit signed/unsigned integer
int64/uint6464-bit signed/unsigned integer

Strings

All strings are stored in memory in UTF-16 format (2 bytes codepoint, or UCS-2) as TCHAR characters.

During serialization, TCHAR characters less than 0xff are stored as a series of 8-bit bytes, and otherwise as 2-byte UTF-16 bytes.

TCHAR

TCAHR is an array of characters.

The TEXT macro is used with TCHAR literals.

FString

FString is a mutable string (similar to std::string).

To create a new FString, use the TEXT macro:

FString MyStr = TEXT("Hello World!").

FText

FText is a string used for localized text.

To create an empty FText, call the FText::GetEmpty function, or the default FText constructor.

To create a new FText, use the NSLOCTEXT macro.

The macro requires a namespace, key, and a value for the default language.

FText MyText = NSLOCTEXT("UI", "Hello", "Hello World!")

The namespace used for localized text can be defined with the LOCTEXT_NAMESPACE macro.

The LOCTEXT macro use the current namespace.

#define LOCTEXT_NAMESPACE "UI"

FText MyText = LOCTEXT("Hello", "Hello World!")

#undef LOCTEXT_NAMESPACE

FName

FName is a string used as an identifier.

FName is a reference to an immutable case-insensitive string in a global string table.

It is stored as an index and is more efficient: smaller storage and fast string comparisons.

To create a new FName, use the TEXT macro.

There is no conversion from FText to FName.

FName strings are used in assets names, material parameters, skeletal mesh bones...

Smart Pointers

Types

Creation

Shared pointers can be created with the MakeShared function in a single memory block if the object has a public constructor.

Otherwise, they can be created with the MakeShareable function, which is less efficient.

Thread Safety

By default, smart pointers are only safe to access on a single thread.

They can optionally be made thread-safe, with the additional ESPMode::ThreadSafe template parameter.

The thread-safety is implemented with atomic operations and is lockless.

Smart pointers should not be used with UObject types.

Assertions

Assertions are declared in /Engine/Source/Runtime/Core/Public/Misc/AssertionMacros.h.

General Asserts

These assertion macros are only compiled in certain build configurations.

check

The check macro executes the expression and, if it results in a false assertion, halts execution.

The macro is enable when DO_CHECK is defined.

checkf

The checkf macro is identical to the check macro.

It has an additional parameter to print a message.

verify

The verify macro is similar to the check macro, but it is enabled regardless of DO_CHECK.

verifyf

The verifyf macro is identical to the verify macro.

It has an additional parameter to print a message.

checkNoEntry

The checkNoEntry macro is used to always assert in invalid execution paths.

A typical example, is in the default block of a switch block.

Development Asserts

These assertion macros are only compiled if DO_GUARD_SLOW is defined.

The checkSlow, checkfSlow, and verifySlow macros are identical to the previously mentioned macros.

The other macros create a callstack report and are only enabled when DO_CHECK is defined.

ensure – Verify the expression.

ensureMsg – Verify the expression with an additional message.

ensurefMsg – Verify the expression with an additional formatted message

Unreal Reflection System

UStruct and UObject types participate in the Unreal Reflection System.

They have the following functionality:

The metadata generated by the Reflection System can only be accessed in Editor builds.

Unreal generates a header file called <TypeName>.generated.h where <TypeName> is the type name that is reflected.

This file must be included as the last included header file.

// Other include files
// ...
#include "MyObject.generated.h"

// Type declaration
// ...

Constructor

Constructor are generally defined in source files.

A constructor can be defined inline in a header file if the type includes the CustomConstructor specifier.

UInterface

Interfaces are C++ classes that are inherited by other classes to share functionality.

They are declared with a I prefix and need the GENERATED_IINTERFACE_BODY() macro as the first statement inside their declaration.

Unreal requires another C++ class with an identical name but the U prefix instead.

That class must:

UINTERFACE()
class UMyInterface : public UInterface
{
    GENERATED_UINTERFACE_BODY()
};

class IMyInterface
{    
    GENERATED_IINTERFACE_BODY()

public:
    ...
};

Usage

To check if a class implemented an interface, call the ImplementsInterface method.

The method returns true of the class implemented the specified interface.

bool bIsImplemented = MyObj->GetClass()->ImplementsInterface(UMyInterface::StaticClass());

To cast an object to a specific interface, use the Cast function.

The function returns null if the interface is not implemented by the class.

IMyInterface* MyInterface = Cast<IMyInterface>(MyObj);
Do not cast to the U-prefixed type as this class is not implemented.

UEnum

C++ enumerations can be marked with the UENUM macro and the BlueprintType specifier to expose them to Blueprint.

Enumeration values can be marked with the UMETA macro and the DisplayName specifier to provide a display name.

UStruct

UStruct objects are plain old data types that support the Unreal Reflection System.

They are not garbage collected.

Typically, they are declared as struct types.

UStruct types need:

USTRUCT()
struct FMyStruct
{
    GENERATED_USTRUCT_BODY()
    ...
};

UObject

All the classes that need to be reflected derive from the UObject class.

They support garbage collection.

Classes that derive from UObject need:

⚠️
The GENERATED_BODY macro sets the member access level to public.
UCLASS()
class UMyComponent : public UActorComponent
{
    GENERATED_BODY()
    ...
};

In overridden methods, the Super typedef can be used instead of the base class name.

void UMyComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}

UObject instances are automatically zeroes before their constructor is called.

Properties can be initialized in their declaration or in the default constructor.

A constructor overload needs a FObjectInitializer structure which can be used to modify the initialization of base types.

Class

Each class that derives from UObject has a singleton UClass that contains all of the metadata about the type.

The UClass instance contains the Class Default Object (CDO) which is an object initialized by the UObject constructor and used as the template object. The properties of the CDO are copied to every new instance of the corresponding UObject.

The UClass is accessible through the GetClass function.

A reference to a UClass can be exposed as a UProperty, but without type safety.

UPROPERTY(EditDefaultsOnly)
UClass* MyType;

For type safety, a reference to a TSubclassOf can be exposed as a UProperty instead.

UPROPERTY(EditDefaultsOnly)
TSubclassOf<UMyType> MyType;

Assigning a UClass to a TSubclassOf performs a runtime check.

Assigning a TSubclassOf to another TSubclassOf performs a compile-time check.

Instances

To create an instance of a UObject class, call the NewObject function with the type of the object as the template argument.

UMyObject* MyObj = NewObject<UMyObject>();

Destroy

To destroy an instance of a UObject class, call the Destroy method.

MyObj->Destroy();

Cast

To cast an instance of an object to a more specific type, call the Cast function.

UPrimitiveComponent* Primitive = MyActor->GetComponentByClass(UPrimitiveComponent::StaticClass());
USphereComponent* SphereCollider = Cast<USphereComponent>(Primitive);
if (SphereCollider != nullptr)
{
    ...
}

AActor

All objects that can be placed into a level derive from the AActor class.

AActor classes provide the following functionality:

AActor contains a hierarchy of UActorComponent components.

The root component is the RootComponent member.

It is a USceneComponent component that contains other components.

The USceneComponent component provides a transform for the AActor (translation, rotation, and scale).

Events

Actors supports different type of events.

Spawn

Creating an instance of an actor in a level is known as spawning.

Actors are spawns:

To spawn an actor, call the UWorld::SpawnActor method with the type of the actor.

There are different overloads, using either a UClass parameter or a template argument.

⚠️
When using a UClass parameter, the type must be derived from AActor; and the return value should be cast to the corresponding derived type.

There are several optional parameters depending on the method overload:

For example, the most simple template method overload:

template<class T>
T * SpawnActor()
AMyActor* MyActor = World->SpawnActor<AMyActor>();

When an actor is spawn:

Destroy

Actors are destroyed:

To explicitly destroy an instance of an actor, call the Destroy method.

MyActor->Destroy();

The lifespan of an actor can be set with SetLifeSpan.

When the lifespan expires the object is destroyed.

virtual void SetLifeSpan
(
    float InLifespan
)

When an actor is destroyed:

To check if an actor is pending kill, store its reference as a FWeakObjectPtr<AActor>, instead of checking explicitly for the RF_PendingKill flag.

AActorComponent

ActorComponents are registered to the Engine to be ticked.

Components created when an Actor is spawned are registered and unregistered automatically.

ActorComponents can be explicitly registered with the RegisterComponent function and unregistered with the UnregisterComponent function.

Destroy

ActorComponents are destroyed:

Garbage Collection

The garbage collector manages a graph of objects that is regularly analyzed.

The root set represents the objects at the root of the graph.

References stored as UProperty are retained (directly or indirectly through a TArray container).

References to objects that have been marked as Pending Kill by the garbage system are automatically set to null (UProperty or TWeakObjectPtr).

Clusters

The garbage collector builds clusters of objects that are all destroyed together.

ℹ️
Combining objects into clusters and releasing the memory at once is more efficient than releasing objects individually.

Cluster merging

Cluster merging is disabled by default, except for StaticMeshActors.

When cluster merging is enabled, the clusters of the object being loaded and the referenced object are combined.

⚠️
In open-world games, many clusters may merge together and form large clusters of objects that are kept in memory.

Collection

When an object is destroyed during the garbage collection, the following functions are called.

  1. BeginDestroy – The object should free up memory and handle multithreaded resources.
  1. IsReadyForFinishDestroy – Determine whether the object is ready to be deallocated. By returning false, the destruction can be deferred to the next garbage collection.
  1. FinishDestroy – Last call before the memory is freed.

Serialization

The serialization of UObject types is performed in the UObject::Serialize function.

The function can be override to implement a custom behavior.

Macros

USTRUCT

USTRUCT exposes a C++ struct.

A USTRUCT can contain UPROPERTY and UFUNCTION members.

Specifiers

UINTERFACE

UINTERFACE exposes a C++ interface.

A UINTERFACE do not contain any member.

The corresponding C++ interface can contain UPROPERTY and UFUNCTION members.

Specifiers

A Blueprintable interface provides functions to be implemented in Blueprint with the BlueprintNativeEvent or BlueprintImplementableEvent specifiers.

UCLASS

UCLASS exposes a C++ class that derive from UObject.

A UCLASS can contain UPROPERTY and UFUNCTION members.

Specifiers

The specifiers are inherited from the base types.

Some specifiers can be explicitly negated in derived types.

Default specifiers (implicit unless negated by another specified):

Other specifiers:

Internal specifiers (used by Epic Games):

UPROPERTY

UPROPERTY exposes a C++ member field.

Transient

The Transient specifier indicates that the property is not saved during serialization.

BlueprintReadOnly

The BlueprintReadOnly specifier exposes the property to Blueprint as a read-only property.

EditAnywhere

The EditAnywhere specifier allows the property to be edited in every editor.

EditFixedSize

The EditFixedSize specified must be used on dynamic arrays, to prevent their size to be edited.

UFUNCTION

UFUNCTION exposes a C++ member method.

BlueprintCallable

The BlueprintCallable specifier exposes the function to Blueprint.

⚠️
Category is required to ensure that the function appears in the context menu of the Blueprint Editor.

BlueprintImplementableEvent

The BlueprintImplementableEvent specifier exposes a Blueprint function to C++.

Unreal generates a C++ function implementation that calls Blueprint (thunk function).

BlueprintNativeEvent

The BlueprintNativeEvent specifier allows a C++ function to be overridden as a Blueprint function.

Unreal also generates a C++ function implementation that calls Blueprint.

The base implementation is named <function>_Implementation where <function> is the function name.

You must declare and implement the base implementation.

It will only be called when Blueprint does not override the function.

// AMyActor.h
UCLASS()
class AMyActor : public AActor
{
    GENERATED_BODY()

public:
    UFUNCTION(BlueprintNativeEvent, Category="MyCategory")
    void MyFunction();

    void MyFunction_Implementation();
    ...
};

// AMyActor.cpp
void AMyActor::MyFunction_Implementation()
{
    ...
}

Ticking

Actors and components are ticked:

During its tick, an Actor ticks all of its Components unless they are in a different group.

Actor are ticked through the Tick function.

Component are ticked through the TickComponent function.

The Actor's tick settings are in the PrimaryActorTick member.

The Component's tick settings are in the PrimaryComponentTick member.

By default, Actors update and Components do not update.

Actors and Components ticking can be enabled or disabled using the bCanEverTick property.

Delegates

Multi-cast delegate functions cannot use return values.

Multiple function delegates are attached and executed all at once by calling the Broadcast function.

⚠️
The execution order of the delegate functions when calling Broadcast is undefined.

Property Specifiers

Events

Events are similar to multi-cast delegates.

They do not have a return value.

They are defined inside classes.

The class that owns an event is the only type able to call the Broadcast function.

Accessors for events are prefixed with On.

Timers

Timers are managed in a global Timer Manager.

A time manager is represented by the FTimerManager class.

The global Timer Manager is accessed using the UGameInstance::GetTimerManager function.

There is a Timer Manager on each Game Instance object, and on each World.

The timers are managed through Time Handlers.

A timer handle is represented by the FTimerHandle class.

Timer handles are to resume and pause a timer, or change the current time.

Time Managers and Timers are not thread-safe.