Asset Manager for Data Assets & Async Loading
What is Asset Manager?
The Asset Manager in Unreal Engine lets you manage your content with more control over loading/unloading and even allows us to load only specific parts of certain assets.
I recommend reading the documentation page as Iāll try not to repeat too much of what is already explained there. Instead Iāll use this article to be more example-driven and from my personal experience.
Your project must define certain classes as Primary Assets (these may often be derived from PrimaryDataAsset but can derive from any UObject). These are the assets you will manage and the system will load/unload any referenced content (also known as āsecondary assetsā) such as meshes and textures. You can turn these āsecondary assetsā (Everything is considered a Secondary Asset by default) into Primary Assets by overriding GetPrimaryAssetId() from UObject.h:
/**
* Returns an Type:Name pair representing the PrimaryAssetId for this object.
* Assets that need to be globally referenced at runtime should return a valid Identifier.
* If this is valid, the object can be referenced by identifier using the AssetManager
*/
virtual FPrimaryAssetId GetPrimaryAssetId() const;
An example of a PrimaryAsset is an AI configuration asset that holds info about a specific monster along with which Actor to spawn for this AI, some attributes, abilities, and perhaps some UI stuff like name and icon.
Here is an example of a PrimaryAsset with MonsterData from my ActionRoguelike on GitHub. The use-case is a basic configuration for an AI to be spawned into the world. The actions are its abilities to be granted.
UCLASS()
class ACTIONROGUELIKE_API URogueMonsterData : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Spawn Info")
TSubclassOf<AActor> MonsterClass;
/* Actions/buffs to grant this Monster */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Spawn Info")
TArray<TSubclassOf<URogueAction>> Actions;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
UTexture2D* Icon;
FPrimaryAssetId GetPrimaryAssetId() const override
{
return FPrimaryAssetId("Monsters", GetFName());
}
};
Another example of a Primary Asset is a Weapon DataAsset that holds variables and soft references such as the weapon Actor Class, damage type, Icon texture, UI Name, Rarity, etc.
If you are looking for a hands-on look, I recommend checking out my Action Roguelike project on GitHub. It contains some async loading examples using Asset Manager. ActionRPG by Epic uses Asset Manager too (but with blocking loads), still useful to see more use-cases on Primary Assets.
Primary Assets
The Asset Manager in Unreal Engine works through Primary Assets that it loads and unloads per our request. Itās similar to soft references in that regard except we use FPrimaryAssetId (a struct with Type and Name) to point to specific assets we wish to load.
You can either use UPrimaryDataAsset or override GetPrimaryAssetId() in any UObject derived class as mentioned earlier to turn it into a Primary Asset. They look very similar code-wise in the MonsterData example earlier.
Data Asset Examples of āMutationsā in WARPSQUAD.
Data Asset Examples of āShip Configurationsā in WARPSQUAD.
PrimaryDataAsset
DataAsset class already set up to support Asset Manager. These assets will purely hold data and no functional logic. You can include Actor classes to spawn, Abilities to grant, UI names, Icons, etc.
You can think of it as descriptors, to describe the AI minion (hitpoints, abilities to grant, actor class to spawn, behavior tree to use) rather than its actual logic and brains.
PrimaryAssetId & PrimaryAssetType
PrimaryAsset Id & Type are both glorified FNames and categorize/identify the assets. This is how you will point to specific assets that you want to load, and is similar to soft references you may be used to.
For example, my ships are of type ShipConfig and one of the Ids that point to a specific data asset could look like ShipConfig:MyPirateShip. (the Id combines the Type:Name) You wonāt be manually typing each Id, instead you can override the GetPrimaryAssetId on your asset in C++ to handle how you want Ids to be generated/handled. You may just want to return the name of your asset file.
Below is an example implementation of setting up the Id for a DataAsset.
FPrimaryAssetId ULZItemData::GetPrimaryAssetId() const
{
return FPrimaryAssetId(ItemType, GetFName());
}
Asynchronous Loading
This aspect is what I could find the least information on when diving into Asset Manager. So Iād like to share some code examples (also available on GitHub) on how to async load your assets.
C++ Async Loading Example
Loading in C++ works by creating a Delegate with your own set of parameters you wish to pass along with it. In the example below I pass in the loaded Id and a vector spawn location.
// Get the Asset Manager from anywhere
if (UAssetManager* Manager = UAssetManager::GetIfValid())
{
// Monster Id taken from a DataTable
FPrimaryAssetId MonsterId = SelectedMonsterRow->MonsterId;
// Optional "bundles" like "UI"
TArray<FName> Bundles;
// Locations array from omitted part of code (see github)
FVector SpawnLocation = Locations[0];
// Delegate with parameters we need once the asset had been loaded such as the Id we loaded and the location to spawn at. Will call function 'OnMonsterLoaded' once it's complete.
FStreamableDelegate Delegate = FStreamableDelegate::CreateUObject(this, &ARogueGameModeBase::OnMonsterLoaded, MonsterId, SpawnLocation);
// The actual async load request
Manager->LoadPrimaryAsset(MonsterId, Bundles, Delegate);
}
The OnMonsterLoaded Function once load has completed:
void ARogueGameModeBase::OnMonsterLoaded(FPrimaryAssetId LoadedId, FVector SpawnLocation)
{
UAssetManager* Manager = UAssetManager::GetIfValid();
if (Manager)
{
URogueMonsterData* MonsterData = Cast<URogueMonsterData>(Manager->GetPrimaryAssetObject(LoadedId));
if (MonsterData)
{
AActor* NewBot = GetWorld()->SpawnActor<AActor>(MonsterData->MonsterClass, SpawnLocation, FRotator::ZeroRotator);
}
}
}
Example taken from open-source Action Roguelike.
Blueprint Async Loading Example
Async loading is a bit easier in Blueprint as there is a neat little node available.

The downside of async loading in Blueprint is that we canāt pass in additional parameters in our own Delegate as easily as we did in C++ example above where we pass in the FVector for spawn location. You can pass in variables from other pins after the load has completed, but Iām unsure about how these variable values are ācapturedā and so should be used with caution as they may have changed since you started the load request a few frames ago.
Asset Bundles
Asset Bundles can be used to categorize the soft references inside your PrimaryAsset to a specific type or use-case. eg. in-game or menu. Sometimes you only need a small part of an asset to be loaded (eg. when viewing a weapon purely in UI without the Mesh rendered anywhere). You can mark up those variables with meta = (Asset Bundles = āUIā) (can be any name you decide) and during the async load request, you may specify to only load 1 or more specific bundles instead of the entire asset when no bundles are specified.
UPROPERTY(ā¦, meta = (AssetBundles = "UI"))
TSoftObjectPtr Icon;
/* Optional Action/Ability assigned to Item. Can be used to grant abilities while this item is active/equipped or to simply run item specific functions */
UPROPERTY(ā¦, meta = (AssetBundles = "Actions"))
TArray> ActionClasses;
/* Optional "Weapon" actor and/or world representation of this object if dropped or equipped by a player */
UPROPERTY(ā¦, meta = (AssetBundles = "Actor"))
TSoftClassPtr ActorClass;
Preloading Assets
The Asset Manager allows us to Preload a set of assets before they are needed. One of the benefits of preloading these assets is so we donāt end up with a visual delay due to the asynchronous load request if we need the asset to be shown right away. Whatās more interesting however is how this Preloading behaves compared to simply calling Load a little early. If we were to Load the assets early, and then lose the Handle to those assets, they will remain loaded indefinitely or until we explicitly unload them through the Asset Manager again (requiring that handle to do so).
Preloading on the other hand, allows to bring the assets into memory just in case weāll need it soon. And when we donāt, we can simply get rid of the handle and itāll automatically be unloaded again.
-
Load - loads the asset and keeps it in memory until explicitly unloaded.
-
Preload - loads the asset and unless you later call Load on those same assets, itāll automatically become unloaded if we lose the handle.
The following is example paraphrased from Nick Darnell to explain this concept more clearly:
In the case of a Treasure Chest, you can Preload all the things it would spawn when opened. If you never open it and it gets destroyed, the handle of the preloaded assets will die too. If they were Loaded instead and the actor went away, it would be more difficult to decide to unload since we donāt know whether something else needs those assets to still be loaded. The preload takes care of that and knows whether it can safely get rid of them by keeping those two concepts separate. If we decided not to preload nor load anything until opening of the chest, we might end up with a delay before we can visibly shoot out the loot like Fortnite does.
Asset Manager Configuration
After configuring your Asset Manager it will automatically discover new PrimaryAssets when added. You setup this configuration in the Project Settings > Asset Manager.
Example Configuration from WARPSQUAD.
What about Streamable Manager?
Asset Manager wraps around the FStreamableManager, which is still a manager you can use for non PrimaryAssets. Itās not a complete replacement, Asset Manager is just solving a specific problem and management.
Auditing Assets
Auditing Assets gives you more insight into how your Primary Assets are setup and used.
right-click on an asset in the content browser lets you āAudit Assetsā¦ā. This gives you some insight into the total size associated with an asset, how often itās used, Ids, Type, etc. Use the buttons at the top to easily filter based on certain criteria.
Audit Assets Window