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
was originally named TAssetPtr
.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.
TSoftClassPtr
was originally named TAssetSubclassOf
.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.
FSoftObjectPath
was originally named FStringAssetReference
.To obtain a string reference from a TSoftObjectPtr
call the ToSoftObjectPath
function.
ToSoftObjectPath
was originally named ToStringReference
.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.
FStreamableManager::SimpleAsyncLoad
(deprecated).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();