Here you'll find anything from defining classes or doing things within classes (classes for Actors & Components).
Here you'll find various pointer types that you can use in Unreal Engine.
If you need a TArray to be initialized with certain values, here is how to do it:
TArray <int32> MyInt32Array
{
{2},
{4},
{8}
};
Bonus: if you wan't to initialize the Array and be sure, it's initialized properly without any values, you may use "{}" (see below).
TArray <int32> MyInt32Array {};
Using pointers this could, in theory, prevent certain errors like "not properly initialized", though maybe just by the compiler.
Variant A:
TArray <int32> MyInt32Array;
if you're using the array immediately afterwards (.Add, .Reserve etc.)
if this is extremely performance critical (every operation counts)
Variant B:
TArray <int32> MyInt32Array {};
if you wanna make it clear, that the array is empty at the point of inizialization
if you want to avoid eventual garbage values inside
If you want to fill an entire TArray with values and already pre-allocate a certain amount of memory for its length, use:
TArray <int32> MyInt32Array;
MyInt32Array.Init(42, 12); // the value "42" will be added "12" times, giving the array a length of 12
If you want to create a dynamic TArray on the heap, but make sure it's as performant as a static array afterwards, use:
TArray <int32> MyInt32Array;
MyInt32Array.Reserve(12);
This...
will only reserve the right amount of memory, so you can easily add new elements and it won't expand automatically until it hits > 12 elements.
will prevent too many changes to the heap
is compatible with pretty much all array based functions/methods within Unreal Engine
To reduce any action that happens on the heap, this setting is extremely important. Let's look at the following example:
TArray <AActor*> Actors;
Actors.Reserve(8);
Here we have told Unreal to create a dynamic array that is stored on the heap and then allocating space for up to 8 elements. If we add more, the size will increase dynamically causing dynamic memory allocation on the heap.
Now let's say, we added some AActor* Actors and later, we want to remove an Actor at a certain index.
Actors.RemoveAt(3);
This will remove the stored pointer to the actor, but it will also shrink the DYNAMIC ARRAY. Meaning: de- and re-allocation on the heap. Why?
Actors.RemoveAt(3, EAllowShrinking::Yes);
As you can see, "EAllowShrinking" is a default setting, that is set to yes.
So what can we do to keep our Array at a size of 8 while not having to do anything on the heap?
Actors.RemoveAt(3, EAllowShrinking::No);
So as a lesson: If you want to remove an Element from an Array without reducing the Arrays' allocated size, always make sure to disallow shrinking whenever possible. This may, depending on the circumstance, save a lot of performance and reduce hitching.
The regular TArray is a dynamically managed Array, meaning it is stored on the Heap.
However, sometimes you know it usually won't exceed a maximum amount of entries, hence storing on the Stack could improve performance.
Especially for using Arrays in functions, it could save some performance. To make use of it:
TArray <AActor*, TInlineAllocator<32>> MyActorArray {};
Now the maximum size of the Array is 32 and it will be stored on the Stack. Additionally, if the size is exceeded, a the Array will be converted into a dynamic one.
This...
... will store it's data on the Stack and save performance.
... may be better than a C-Style array, due to various validity checks and memory management by Unreal Engines' framework.
... can exceed the maximum size, but will then become a dynamic array being stored on the heap.
... does NOT work with UPROPERTY!
... should probably not be used with very large Arrays or ones, where the size may easily be exceeded.
**untested
The regular TArray is a dynamically managed Array, meaning it is stored on the Heap.
However, sometimes you know it won't exceed a maximum amount of entries, hence storing on the Stack could improve performance.
Especially for using Arrays in functions, it could save some performance. To make use of it:
TArray <AActor*, TFixedAllocator<32>> MyActorArray {};
Now the maximum size of the Array is 32 and it will be stored on the Stack and it can NOT be exceeded!
This...
... will store it's data on the Stack and save performance.
... may be better than a C-Style array, due to various validity checks and memory management by Unreal Engines' framework.
... must have checks to prevent it from overflowing (index counter or an if check like MyActorArray.Num() < MyActorArray.Max()**)
... does NOT work with UPROPERTY!
... should probably not be used with very large Arrays or ones, where the size may easily be exceeded.
If you need a TMap to be initialized with certain values, here is how to do it:
TMap <int32, FString> StringMap
{
{1, "Name 1"},
{2, "Name 2"},
{3, "Name 3"}
};
Bonus: if you use a Struct as Value, here a small example:
TMap <FString, FMyStruct> StructMap // FMyStruct contains: Enemy Max Health, Basic Damage, Attack Speed (all int32).
{
{"Enemy 1", {25, 4, 7}},
{"Enemy 2", {30, 2, 9}},
{"Enemy 3", {100, 3, 3}},
};
As a point of reference, the difference between Unreals' TMap and std::map in C++ are:
TMap uses a hash table to store and retrieve the elements, while std::map uses a balanced binary search tree (usually a red-black tree).
TMap does not preserve the order of the elements, while std::map sorts the elements by their keys according to a comparison function.
TMap requires the elements to be hashable and comparable, while std::map only requires the keys to be comparable.
TMap has faster lookup and membership testing, while std::map has faster traversal and iteration.
TMap has a template parameter for the allocator, which controls the memory allocation behavior, while std::map has a template parameter for the key comparison function, which defines the order of the elements.
TMap is NOT a linked list, as in: it does not store the address for the previous and or next entry.
To loop through a TMap, here an example:
TMap <int32, FVector> MyTmap;
for (const auto& MapElement : MyTmap)
{
const int32 NewInt = MapElement.Key; // access Key and assign
const FVector NewVector = Map.Value; // acces Value and assign
}
The const part doesn't really do anything, it just shows how to access the Key and Value of the TMap with MapElement.Key und MapElement.Value.
Not yet fully tested!
In order to create TArrays from a TMap, use the following:
TMap <AActor*, int32> MyTmap; // TMap to be used
TArray<AACtor*> KeyArray; // Inizialized array
MyTmap.GenerateKeyArray(ActorArray); // Uses KeyArray Reference to fill
TArray<int32> ValueArray;
MyTmap.GenerateValueArray(ValueArray);
If you need a TSet to be initialized with certain values, here is how to do it:
TSet<int64*> PowerOfTwos {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192};
As you can see it's identical to a TArray.
As a good point of reference, the differences between a TSet and a std::list in C++ are:
TSet does not allow duplicate elements, while std::list does.
TSet does not preserve the order of the elements, while std::list does.
TSet requires the elements to be hashable and comparable, while std::list does not.
TSet has faster lookup and membership testing, while std::list has faster traversal and iteration.
It's as simple as a for loop for a regular TArray:
TSet<AActor*> ActorSet {...}; // define Set
for (auto Actor : ActorSet) // instead of auto, AActor* would also work just fine (this example)
{
Actor->GetName(); // anything you want to do
}
An example for a function that converts a TSet to an Array:
static TArray<AActor*> ConvertSetToArray(TSet<AActor*> Set)
{
TArray<AActor*> Array = Set.Array(); //alternatively auto can be used as a variable type (untested)
return Array;
}
If using some Unreal Enums like for example "EDrawDebugTrace", simply using them as a variable type does not work (source).
If you get an error, the following example will work properly:
TEnumAsByte<EDrawDebugTrace::Type> EnumVariableName;
If you have a C++ Enum as an input for a function, to get its name use:
FString EnumName = UEnum::GetValueAsString(EnumInput);
Important: this will return the full string, for example:" EValue::String1"!
To fix this, you can use the following AS AN EXAMPLE:
const FString StringValueRaw = UEnum::GetValueAsString(ViewMode);
FString Left;
FString Right;
StringValueRaw.Split("::", &Left, &Right, ESearchCase::IgnoreCase, ESearchDir::FromStart);
Now Right is the value itself.
As an example on how to create a UStruct, simply create a new source file (.h) and copy-paste the following as an example:
#pragma once
#include "FMyVectorInt4D.generated.h"
USTRUCT(BlueprintType, Category = "MyCategory")
struct FMyVectorInt4D
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite) int64 X;
UPROPERTY(BlueprintReadWrite) int64 Y;
UPROPERTY(BlueprintReadWrite) int64 Z;
UPROPERTY(BlueprintReadWrite) int64 W;
};
This example is used to create a 4D Vector based on 4 64 bit integers. It can be used on C++ and Blueprints.
A simple caviat: If you want to use it as an exposed value for a Blueprint, you need to add "EditAnywhere" besides "BlueprintReadWrite".
Now the above is working okay, but we can't simply just say something like MyVectorInt4D = FMyVectorInt4D(1,2,3,4).
To make that possible, we need to create two constructors in general: one that uses default values, and one that can easily be filled with values.
Use the above, but add the following lines of code before the variable definitions
FMyVectorInt4D() // Default constructor
{
X = 0;
Y = 0;
Z = 0;
W = 0;
}
FMyVectorInt4D(int64 InX, int64 InY, int64 InZ, int64 InW) // Constructor with all values
{
X = InX;
Y = InY;
Z = InZ;
W = InW;
}
As a bonus, you could also create a constructor to take in one value and assign it to all variables:
FMyVectorInt4D(int64 InXYZW)
{
X = InXYZW;
Y = InXYZW;
Z = InXYZW;
W = InXYZW;
}
Also: you don't need to use FORCEINLINE or explicit FORCEINLINE to make it work. UStruct seems to handle that on its own.
Strong Object Pointers (TStrongObjectPtr<> Name) are perfect for UObjects not flagged by UPROPERTY() to be handled automatically by the Unreal Engine garbage collection. Or in other words: if you want to simply use a pointer without UPROPERTY() or make a fixed sized array, use TStrongObjectPtr to let Unreal handle the garbage collection of UObjects. For regular types like int, float etc. TSharedPtr are the better option.
Examples:
// New ptr managed by Unreal, C++ only.
TStrongObjectPtr<USceneComponent> MyStrongObjectPtr;
// Fixed sized Array that can't use "UPROPERTY()",
// but still needs to be garbage collected when out of scope.
TArray<TStrongObjectPtr<USceneComponent>, TInlineAllocator<16>> MyStrongObjectFixedSizeArray;
// Adding to a fixed size Array with TStrongObjectPtr
USceneComponent* MyRawPointer {};
MyStrongObjectFixedSizeArray.Add(TStrongObjectPtr(MyRawPointer)); // using Constructor
Unique Pointers (TUniquePtr<> Name) are awesome to make sure, that a memory address to for example an Object can only be owned by one pointer. This can help making sure there is, for example, only one Actor who can currently own a component.
Here are the basics:
They cannot easily be shared or be used as a function input.
They can not be made a UPROPERTY().
They can be initialized to {nullptr} / = nullptr
To male another TUniquePtr on an address, they ownership has to be switched
If .Reset() is used to take ownership of another pointer, the pointer won't automatically be set to a nullptr!
Once out of scope, the TUniquePtr will destroy what points to (Actor etc.).
Extremely important: make use of Release, also on EndPlay, cause some destructors may otherwise crash the game/engine!
Examples:
TUniquePtr<UWorld> MyWorldUnique (GEngine->GameViewport->GetWorld()); // UniquePtr from a RawPtr
TUniquePtr<int64> MyUniquePtr1 = MakeUnique<int64>(65); // UniquePtr from a value
TUniquePtr<int64> MyUniquePtr2 = MoveTemp(MyUniquePtr1); // Moving Ownership from Ptr1 to Ptr2, // Ptr1 now nullptr
TUniquePtr<AActor> MyUnuiqueActor {}; // Define a UniquePointer to store another pointer in.
MyUnuiqueActor.Reset(OtherActor); // Move other Actor to the Unique Pointer.
// Other Pointer will be set to nullptr.
MyUniqueActor.Reset(); // Destroy AActor if withing scope.
MyUniqueActor.Release(); // Release Ownership ans set MyUniqueActor to nullptr.
MyUniqueActor.Get(); // Get pointer to the Object without moving ownership.
How to safely use TUniquePtr.:
1a. If used as a object-variable: TUniquePtr<AActor> MyActor;
1b: If used within scope: same as above.
2: Transfering ownership of another Pointer MyActor.Reset(OtherActor);
3: Now you can make use of it however you see fit (some actions may require MyActor.Get())
4: You could also move ownership to another TUniquePointer using MoveTemp(MyActor)
5a: If using a TUniquePtr to a Component attached to an AActor, use MyActor.Release() before using any Destroy command!
5b: To avoid crashing the editor when exiting the game, make sure to add MyActor.Release() to the EndPlay Event!
A few examples of the usage. Also, a great tutorial on C++ Unique, Shared and Weak Pointers.
TSharedPtr<UWorld> MyWorldShared (GEngine->GameViewport->GetWorld()); // SharedPtr from a RawPtr
TSharedPtr<int64> MySharedPtr1 = MakeShared<int64>(64); // SharedPtr from a value
TWeakPtr<UWorld> MyWorldWeak = MyWorldShared; // WeakPtr from SharedPtr
TWeakPtr<int64> MyWeakPtr1 = MySharedPtr1; // WeakPtr from SharedPtr
int64* MyInt64Ptr1 = MySharedPtr1.Get(); // Get RawPtr from SharedPtr
int64* MyInt64Ptr2 = MyUniquePtr2.Get(); // Get RawPtr from UniquePtr MyUniquePtr2.Release(); // Release memory of the UniquePtr
// MyUniquePtr2 now nullptr
TObjectPtr<UWorld> MyWorldObject = GEngine->GameViewport->GetWorld(); // Get ObjectPtr from RawPtr
To make sure, an Object is only loaded into memory, when needed, TSoftPtr are the best solution. By either loading them synchronously or asynchronously the will only be loaded when required. Hint: you can load them as often as you want, because if they're already in memory, Unreal will just return the already loaded object.
Example: We will try to get the location of an Actor in the World.
// Hard Reference/Hard Pointer
AActor* LocationActorRef;
// Soft Reference/Soft Pointer
TSoftObjectReference<AActor> LocationActorSoftRef;
// Function for Hard Reference
FVector GetActorLocationHardRef()
{
return LocationActorRef->GetActorLocation();
}
// Function for Sync Soft Reference
FVector GetActorLocationSoftRefSync()
{
AActor* LocationActorRef = LocationActorRef.LoadSynchronous();
return LocationActorRef->GetActorLocation();
}
// Function for Async Soft Reference (WORK IN PROGRESS)
void GetActorLocationSoftRefAsync()
{
auto Handle1 = UAssetManager::GetStreamableManager().RequestAsyncLoad(
LocationActorSoftRef.ToSoftObjectPath(),
FStreamableDelegate::CreateUObject(this, &ThisClass::UseAsyncLocation));
}
void UseAsyncLocation(FVector Location)
{
AActor* LocationActorRef = LocationActorSoftRef.Get();
FVector Location = LocationActorRef->GetActorLocation();
// Do something with the Location...
}
Now imagine, you want to set a Soft Object Reference from a Hard Reference, the following is a good example on how to achieve this:
AActor* HardObjectPtr = this;
TSoftObjectPtr<AActor> SoftObjectPtr(HardObjectPtr);