When we reference an asset, object or class in Unreal, we can use a hard reference or a soft reference. The choice affects how we access it and when it needs to be loaded.
Let’s go through how to create each type and when to use it.
What Is a Hard Object Reference?
Hard references are direct pointers to an asset or object, set at compile time. Deleting the referenced asset, object or class means the code will no longer compile.
Access is generally faster than with a soft reference, as there is no extra lookup.
To create one, declare a variable of the asset, object or class type and assign the reference. For a character Blueprint in C++, this would be an AMyCharacter variable, where AMyCharacter is the class of that Blueprint:
#include "MyCharacter.h"
class AMyClass
{
public:
AMyCharacter* MyCharacterRef = nullptr; // Declare a hard reference to MyCharacter blueprint
...
};
void AMyClass::MyFunction()
{
MyCharacterRef = Cast(StaticLoadObject(AMyCharacter::StaticClass(), NULL, TEXT("/Game/MyContent/MyCharacter.MyCharacter"), NULL, LOAD_None, NULL)); // Assign the hard reference to the specific MyCharacter blueprint
...
}
Soft Object References and TSoftObjectPtr
Soft references are indirect pointers set at runtime. Deleting the referenced asset, object or class still lets the code compile. Access is generally slower, since finding the target requires an extra lookup at runtime.
We can create them with TSoftObjectPtr, TSoftClassPtr or TSoftAssetPtr. These let us specify the path to the asset, object or class to reference at runtime, instead of hard-coding it:
#include "SoftObjectPtr.h"
#include "MyCharacter.h"
class AMyClass
{
public:
TSoftObjectPtr MyCharacterRef = nullptr; // Declare a soft reference to MyCharacter blueprint
...
};
void AMyClass::MyFunction()
{
MyCharacterRef = TEXT("/Game/MyContent/MyCharacter.MyCharacter"); // Assign the soft reference to the specific MyCharacter blueprint
...
}
When Should You Use Hard References?
Core engine objects and the initial references needed to run the game are candidates for hard references. They are objects we expect to exist and remain accessible.
Ex: the player character that other systems always need to access, or a required game asset that must stay loaded.
The trade-off is that those objects must exist. Changing an object or its location can break references to it.
When Should You Use Soft References?
Soft references let us load and unload objects as needed. This is useful for maps and other large assets that take up memory and processing power.
In an open-world game, we can load individual levels when needed instead of keeping them all in memory. An asset used only in certain situations can also be loaded for that situation and unloaded afterwards.
This also fits optional assets that should be allowed to change or be deleted without breaking the code. We do need to make sure an object is ready before using it. If it has not loaded in time, we can get errors or performance problems.
Resolving a soft reference at runtime is the next step. I have not covered the Streamable Manager’s asynchronous RequestAsyncLoad versus a blocking LoadSynchronous() here. For larger projects, the Asset Manager and Primary Asset IDs also handle streaming and reference tracking.
Recap
Use a hard reference when the asset has to be there and loading it up front is fine: the player character, core gameplay classes, anything a system can’t run without. Hard references are faster to resolve, and they fail at compile time if the target goes missing, so you find out early.
Use a soft reference (TSoftObjectPtr, TSoftClassPtr) when you want to load and unload on demand: levels, large meshes, optional or situational content. The cost is an extra lookup and a load at runtime, and the asset might not be ready the instant you ask for it.
The bigger reason this matters is memory. A hard reference pulls the whole dependency chain into memory alongside the asset that holds it. A soft reference breaks that chain, which is what keeps load times and memory down on a large project.


