Architecture
Source Code
The Unreal types have prefixes to indicate their kind:
- U – Classes that derive from
UObject
.
- A – Classes that derive from
AActor
.
- S – Classes that derive from
SWidget
.
- T – Template classes.
- I – Interface types.
- E – Enumerations.
- F – Any other type (mostly POD structures).
Some variables also have prefixes:
- b – Boolean variable.
Containers
Unreal containers are templated classes.
Type | Container |
---|---|
Vector | TArray |
List | TList |
Map | TMap |
Unordered Map | TMultiMap |
Set | TSet |
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 a TArray
is declared, no memory is allocated.
Insertions
A TArray
can be populated using different methods:
- The
Init
function fills the array with a number of copies of an element.TArray<int32> IntArray; IntArray.Init(10, 5);
- The
Add
andEmplace
functions create new elements at the end of the array.Emplace
avoids creating a temporary variable.Add
should only be used with trivial types.TArray<FString> StrArr; StrArr.Add(TEXT("Hello")); StrArr.Emplace(TEXT("World"));
- The
Append
function adds multiple elements from another array or a pointer to a C-style array.FString Arr[] = { TEXT("Hello"), TEXT("World") }; TArray<FString> StrArr; StrArr.Append(Arr, ARRAY_COUNT(Arr));
- The
Insert
method adds a single element at a given index.StrArr.Insert(TEXT(" "), 1);
- The
AddUninitialized
andInsertUninitialized
functions add uninitialized space to the array. They do not call the constructor of the element type. They can be used to avoid calling constructors when performing a memory copy.int32 Arr[] = { 1, 2, 3, 4 }; TArray<int32> IntArr; IntArr.AddUninitialized(4); FMemory::Memcpy(IntArr.GetData(), Arr, 4 * sizeof(int32));
Deletions
- The
Empty
function removes all the elements from the array.
- The
Remove
function removes all the elements that are equal to the specified element by shuffling other elements.
- The
RemoveSwap
function reduces the overhead of shuffling by swapping.
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.
- The
Num
function returns the number of elements in the array.
- The
SetNum
function resizes the array, and if the array is larger, the new elements are created using their type's default constructor.
- The
GetData
function returns a direct pointer to the data in the array.
Searching
Arrays can be searched for specific elements with:
- The
Contains
function checks if the array contains a specific element.
- The
ContainsByPredicate
function checks if the array contains an element that matches a specific predicate.
- The
Find
andFindLast
functions have 2 overloads.- They returns a boolean and the index of the first element found.
- They returns the index of the first element found, or
INDEX_NONE
if no element was found.
Sorting
Arrays can be sorted with:
- The
Sort
function using a quicksort.
- The
HeapSort
function using a heap sort.
- The
StableSort
function to guarantee the relative order of equivalent elements after sorting.
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.
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.
A map can be sorted:
- By keys using the
KeySort
function.
- By values using the
ValueSort
function.
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.
- Any type with a
GetTypeHash
function andoperator==
can be used as a key.
- The hash values are used to uniquely identify value in a
TMap
and aTSet
.
The hash function takes a const pointer or reference to a type and returns a uint32
hash value.
Numeric Types
Types | Kind |
---|---|
int8/uint8 | 8-bit signed/unsigned integer |
int16/uint16 | 16-bit signed/unsigned integer |
int32/uint32 | 32-bit signed/unsigned integer |
int64/uint64 | 64-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
TUniquePtr
is a unique pointer. It owns the object it references.TUniquePtr
can transfer ownership to its reference, but cannot share it.TUniquePtr
are the same size as C++ pointers.
TSharedPtr
is a shared pointer. It owns the object it references, and can be set to null.TSharedPtr
can share its reference.TSharedPtr
are twice the size as C++ pointers (and a 16-byte reference controller).
TSharedRef
is a shared reference. It is similar to aTSharedPtr
but it always references a non-null object. ATSharedRef
can be created from aTSharedPtr
.
TWeakPtr
is a weak pointer. It is similar to aTSharedPtr
but it does not owns the object it references. A weak pointer can become null at any time. ATSharedPtr
can be created from aTWeakPtr
by calling thePin
function. Weak pointers can break reference cycles.
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.
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:
- Reflection of properties and methods.
- Serialization of properties.
- Networking support.
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:
- Derive from the
UInterface
class.
- Include the
UINTERFACE()
macro before the declaration.
- Include the
GENERATED_UINTERFACE_BODY()
macro as the first statement inside the declaration.
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);
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:
- The
USTRUCT()
macro before their declaration.
- The
GENERATED_USTRUCT_BODY()
macro as the first statement inside their declaration.
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
UCLASS()
macro before their declaration.
- The
GENERATED_BODY()
macro as the first statement inside their declaration.
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:
- Instantiation in a level (at design-time or run-time).
- Network replication.
- Contain components.
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.
BeginPlay
– Called when the level starts ticking.
Tick
– Called once per frame.
EndPlay
– Called when the actor is being removed from a level.
Spawn
Creating an instance of an actor in a level is known as spawning.
Actors are spawns:
- When a level is loaded through
LoadMap
.
- When a level is streamed through
AddToWorld
.
- During Play in Editor through
PostDuplicate
.
- Explicitly through
UWorld::SpawnActor
.
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.
There are several optional parameters depending on the method overload:
- A name for the actor.
- A location and rotation in the world.
- A template actor to provide default property values.
For example, the most simple template method overload:
template<class T>
T * SpawnActor()
AMyActor* MyActor = World->SpawnActor<AMyActor>();
When an actor is spawn:
OnActorSpawned
is broadcast onUWorld
.
BeginPlay
is called on the actor.
Destroy
Actors are destroyed:
- When a level is unloaded.
- When Play in Editor is ended.
- The actor's lifetime has expired.
- Explicitly through
Destroy
.
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:
EndPlay
is called on the actor.
- The actor is removed from the level and marked as pending kill (
RF_PendingKill
).
- The actor is cleaned up during the next garbage collection.
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:
- When the actor is destroyed.
- Explicitly by calling the
UActorComponent::DestroyComponent
function.
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.
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.
Collection
When an object is destroyed during the garbage collection, the following functions are called.
BeginDestroy
– The object should free up memory and handle multithreaded resources.
IsReadyForFinishDestroy
– Determine whether the object is ready to be deallocated. By returning false, the destruction can be deferred to the next garbage collection.
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
BlueprintType
– Exposes the struct as a type that can be used in Blueprint.
UINTERFACE
UINTERFACE
exposes a C++ interface.
A UINTERFACE
do not contain any member.
The corresponding C++ interface can contain UPROPERTY
and UFUNCTION
members.
Specifiers
BlueprintType
– Exposes the interface as a type that can be used in Blueprint.
MinimalAPI
– Only the type information is exported for use by other modules.
Blueprintable
– Exposes the interface to be implemented in Blueprint.
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):
NotBlueprintable
– Do not expose the class as a base class for Blueprints.
Other specifiers:
Abstract
– Declare an abstract base class. Cannot be instantiated in a level.
Blueprintable
– Exposes the class as a base class in Blueprint.
BlueprintType
– Exposes the class as a type that can be used in Blueprint.
CustomConstructor
– Prevents the automatic generation of the constructor declaration.
MinimalAPI
– Only the type information is exported for use by other modules.
Placeable
/NotPlaceable
– Whether the class can be created in the Editor (level, Blueprint, UI scene). The flag is propagated to child classes.
Transient
/NonTransient
– The class is non-persistent and is not serialized. The flag is propagated to child classes.
Internal specifiers (used by Epic Games):
Intrinsic
– Indicates that the class do not contain generated code from the UnrealHeaderTool.
NoExport
– Indicates that the class is not included in the generated C++ header file.
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.
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:
- Once per frame
- Or at a specified ticking interval
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.
Property Specifiers
BlueprintCallable
– can be called in Blueprint.
BlueprintAssignable
– can be assigned in Blueprint.
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.