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
TSoftObjectPtrinstance.
- Otherwise, it can be loaded from a
TSoftObjectPtrinstance, and accessed directly later on.
Usage
- To check if a
TSoftObjectPtrcontains a valid object, call theIsValidfunction.
- If there is a live
UObject, call theGetfunction 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
TSoftClassPtrcontains a valid object, call theIsValidfunction.
- If there is a live
UObject, call theGetfunction to dereference theTSoftClassPtr.- The
Getmethod returns an instance to aUClassinstead of aUObject.
- Call the
GetDefaultObjectfunction from theUClassobject to get a pointer to the derivedUObjectinstance.
- 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
RequestAsyncLoadand store the handle.
- Call
IsValidto 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
IsValidto check if the handle is still valid.
- Call
IsLoadingInProgressto check if the asset is being loaded.
- Call
HasLoadCompletedto 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
RequestAsyncLoadwith 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();