Assets Streaming
Overview
In the Unreal Engine, when a UObject
references other Assets, they are loaded in memory by default.
For example, when a UPROPERTY
property is defined in a UObject
, the reference is loaded in memory when the owner itself is being loaded.
UPROPERTY(EditAnywhere)
UMyAsset * MyAsset;
In some cases, we want to define an Asset dependency, but we need to defer the loading of these Assets when they are actually needed.
TSoftObjectPtr
Instead of storing a pointer to an Asset directly (hard pointer), we store it inside a TSoftObjectPtr
object (soft pointer or lazy pointer).
TSoftObjectPtr
is a template class that is declared with an asset type and can be stored as a UPROPERTY
property.
TSoftObjectPtr
contains a reference to an Asset if it is loaded in memory, and additional information to load it on demand.
UPROPERTY(EditAnywhere)
TSoftObjectPtr<UMyAsset> MyAssetPtr;
- When an Asset is loaded, it can be accessed directly from the
TSoftObjectPtr
instance.
- Otherwise, it can be loaded from a
TSoftObjectPtr
instance, and accessed directly later on.
Usage
- To check if a
TSoftObjectPtr
contains a valid object, call theIsValid
function.
- If there is a live
UObject
, call theGet
function to dereference theTSoftObjectPtr
.
- If there is not live
UObject
, we need to stream it into memory.
The name of the Asset can be obtained by calling the GetAssetName
function.
TAssetSubclassOf
To store a reference to a subclass, use the TSoftClassPtr
object instead.
TSoftClassPtr
works like a TSubclassOf.
TSoftClassPtr
is a template class that is declared with a base asset type and can be stored in a UPROPERTY
property.
UPROPERTY(EditAnywhere)
TSoftClassPtr<UMyAsset> MyAssetPtr;
Usage
- To check if a
TSoftClassPtr
contains a valid object, call theIsValid
function.
- If there is a live
UObject
, call theGet
function to dereference theTSoftClassPtr
.- The
Get
method returns an instance to aUClass
instead of aUObject
.
- Call the
GetDefaultObject
function from theUClass
object to get a pointer to the derivedUObject
instance.
- The
Loading
Asynchronous
To load an asset asynchronously, we need to use FStreamableManager
.
The FStreamableManager
object streams assets in and keeps them in memory.
A good place to store it, is in the GameInstance
.
FStreamableManager StreamableManager;
FStreamableManager
loads Assets from a string reference represented by FSoftObjectPath
(also known as soft reference).
Internally, FSoftObjectPath
stores a FName
pointing to a top level asset and optionally, a subobject path.
To obtain a string reference from a TSoftObjectPtr
call the ToSoftObjectPath
function.
FSoftObjectPath SoftObjectPath = MyAssetPtr.ToSoftObjectPath();
To load an asset from a string reference, call the FStreamableManager::RequestAsyncLoad
function.
The RequestAsyncLoad
function has several overloads:
- Loads a single asset, or a list of assets.
- Loads asynchronously or synchronously.
- Calls either a delegate or a lambda function when loading is complete.
Streaming Handle
The function returns a FStreamableHandle
handle.
As long as the handle is active, the loaded assets will stay in memory.
The handle should be stored in the owner of the loaded asset.
TSharedPtr<FStreamableHandle> Handle;
To load an asset using the FStreamableHandle
:
- Call
RequestAsyncLoad
and store the handle.
- Call
IsValid
to check if the handle is valid.
FStreamableHandle Handle = StreamableManager.RequestAsyncLoad(SoftObjectPath);
if (!Handle.IsValid())
{
return;
}
To obtain the asset object, the handle need to be polled to check if the loading has completed.
- Call
IsValid
to check if the handle is still valid.
- Call
IsLoadingInProgress
to check if the asset is being loaded.
- Call
HasLoadCompleted
to check if the loading is complete.
- The loading can be cancelled by calling
CancelHandle
.
- When loading is complete, the asset object can be obtained by calling
GetLoadedAsset
.
if (Handle->IsValid && Handle->HasLoadCompleted())
{
// Use the loaded asset
UObject* Obj = Handle->GetLoadedAsset();
...
}
Lambda
To load an asset with a lambda function, call RequestAsyncLoad
with a lambda.
The lambda will be called when the loading is complete.
StreamableManager.RequestAsyncLoad(
SoftObjectPath, []()
{
// Use the loaded asset
...
});
Delegate
To load an asset with a delegate:
- Create a delegate with
FStreamableDelegate::CreateUObject
.
- Call
RequestAsyncLoad
with the delegate.
For example, if the owner is named UAssetLoader
:
UAssetLoader::Load()
{
StreamableManager.RequestAsyncLoad(
SoftObjectPath,
FStreamableDelegate::CreateUObject(this, &UAssetLoader::OnLoadComplete)
);
}
UAssetLoader::OnLoadComplete()
{
// Use the loaded asset
...
}
Calling RequestAsyncLoad
with bManageActiveHandle
set to true indicates that the manager should keep the handle alive.
Synchronous
FStreamableManager
can also load Assets synchronously.
To load an asset synchronously, call the RequestSyncLoad
function.
The function returns a FStreamableHandle
.
Handle = StreamableManager.RequestSyncLoad(SoftObjectPath);
The handle must be released when the asset is not needed, or when the owner is destroyed.
To release a handle, check that it is valid with IsValid
and call ReleaseHandle
.
if (Handle.IsValid())
{
Handle->ReleaseHandle();
}
Another way to load an asset synchronously is to call the LoadSynchronous
function either from the FStreamableManager
or from the TSoftObjectPtr
.
The function loads the asset, and returns the asset object represented by the asset pointer.
UMyAsset* MyAsset = MyAssetPtr.LoadSynchronous();