[Solved] Troubleshooting Primary Assets

I recently wrote a guide on UDN to help someone troubleshoot Primary Assets, pasting it here for a wider audience.

What is a Primary Asset in Unreal?

First read this primer, Asset Management in Unreal Engine | Unreal Engine 5.3 Documentation.

A Primary Asset in Unreal is any asset that fulfills these two conditions:

1: It’s any asset that has had the “Primary Asset Type” and “Primary Asset Name” Asset Registry values filled.

To populate these values for any asset it needs to have a native class in the parent hierarchy that overrides the GetPrimaryAssetId() function (or responds to the FCoreUObjectDelegates::GetPrimaryAssetIdForObject delegate for that object).

You can see immediately if the asset has its Primary Asset fields correctly set by hovering over it in the Content Browser after saving it, since they get saved into the Asset Registry:

image

We have a helper base class which we recommend using, UPrimaryDataAsset. It has neat functionality like automatically filling in the PrimaryAssetID based on the asset parent class and the asset name, and it adds support for Asset Bundles.

If you want to use UPrimaryDataAsset then you HAVE to create a C++ that extends it as the base class for any assets, consider UPrimaryDataAsset as an Abstract class. You can also have a parent Blueprint class as the parent for your assets, but that Blueprint class also needs to have your custom C++ class as the parent, not UPrimaryDataAsset directly. I generally recommend against having a BP parent class for Data Assets due to some rare bugs happening with circular BP references causing data loss.

2: It needs to be scanned by the Asset Manager.

You make the Asset Manager scan it by adding a “Primary Asset Types to Scan” entry in the Project Settings under Game → Asset Manager.

Remember to also put in the folder path of the assets to scan.

If the assets you want to scan are Blueprints then you need to check the “Has Blueprint Classes” checkmark. Blueprints need special handling compared to other types (like DataAssets, Maps, or anything really) since they need to be compiled and the generated class is treated differently than other native classes.

Why use Primary Assets?

There are a few reasons why someone might want to use Primary Assets in their project:

Dynamic querying and loading

Primary Assets can be loaded via the Asset Manager using only their ID, and you can query all Primary Asset of each class via the Asset Registry. This allows you to programmatically and dynamically query and load primary assets without first having an array of references to them.
An example would be to have all possible random collectibles that can spawn from a chest be Primary Assets. This makes it easy to add more collectibles without having to remember to add them to an array somewhere.

Asset Bundle support

Asset Bundles in Unreal are a bit different from Asset Bundles in other engines, it’s not a bundle of assets in a file like Unreal’s pak chunks are. Instead, it’s the ability to bundle specific assets with the loading operation of a Primary Asset (specifically one inheriting from UPrimaryDataAsset, which is the class that adds that ability.)

Side note: If you need a different parent class but still want to have Asset Bundling support just copy the asset bundling code from UPrimaryDataAsset into your other class.

For an example, lets say we have a UWeapon Primary Asset class. This UWeapon class has a soft object pointer to an image of the weapon that should only be shown in the store UI. Otherwise we don’t need to load that image. But sometimes when we load the UWeapon asset it’s because we want to show the weapon model in-game, then we have another soft object pointer to the 3d mesh that should only be loaded when a game match is starting.

Soft object pointers are by default not loaded with the parent asset, you’ll need to manually load them yourself. But using Asset Bundling you “bundle” the loading of a specific soft object pointer with the primary asset itself.

Here’s how the UPROPERTIES might look like in your Weapon class:

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StaticMesh", meta = (AssetBundles = "InMatch"))
TSoftObjectPtr<UStaticMesh> WeaponMesh;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Textures", meta = (AssetBundles = "Store"))
TSoftObjectPtr<Texture2D> WeaponStoreIcon;

By default those are not loaded with the UWeapon asset. But if you use the Asset Manager to load your UWeapon Primary Asset you can now specify Bundles to load additionally:

TSharedPtr<FStreamableHandle> WeaponHandle = UAssetManager::Get().LoadPrimaryAsset(FPrimaryAssetId(TEXT("Weapon:Shotgun")), TArray<FName> { TEXT("Store") }, FStreamableDelegate::CreateUObject(this, &UWeaponManager::OnWeaponLoaded));

The “OnWeaponLoaded” delegate will only trigger once the weapon asset AND the store texture have been loaded.
The bundled assets will stay loaded as long as either that WeaponHandle exists, or as long as something else references those assets. Otherwise it will get garbage collected.

Set Cooking Rules

By default Unreal will only cook all Maps in your project and all assets those map reference (I’m over simplifying, there are other ways assets are included in a build that I’m skipping over.)
That means if you somehow don’t reference your assets then they won’t be cooked. But what if you have lots of collectibles, weapons, or enemies that are only ever loaded dynamically from their IDs? That’s where PrimaryAssetRules come in. You simply set up your Primary Assets in the “Primary Asset Types to Scan” property in the project settings and now you can specify that the cooking rule for Collectibles is “AlwaysCook”.

A workaround would be to put them into an AlwaysCook folder, which would work, but I recommend not using AlwaysCook folders and instead marking assets as Primary Assets with their own cook rules.

You are also able to specify different pak chunks for your primary assets, separating the pak files in your game.

For more info about Primary Assets please check out these resources:
Asset Management in Unreal Engine | Unreal Engine 5.3 Documentation
Asset Manager for Data Assets & Async Loading - Tom Looman

10 Likes