Download

Let's talk about the 4.17 Asset Manager and Asset Management in general

Hi,
So as you may know, the Asset Manager was officially taken out of early access in 4.17 as “a unique, global object that exists in the Editor as well as in packaged games, and can be overridden and customized for any project. It provides a framework for managing Assets that can divide content into chunks that make sense in the context of your project, without losing the advantages of Unreal Engine 4’s loose package architecture.

You may read more about the Asset Manager in the official documentation, as well as in this pre-release PDF (which is slightly outdated but still somewhat complementary on some details).

If you want to be up to speed really quickly about the Asset Manager, it roughly goes like this:

  • You have Primary Assets and Secondary Assets.

  • Primary Assets are UObjects that override the GetPrimaryAssetId() function (keep that in mind, this is important), and that you may want to explicitly tell to load at some point.

  • Secondary Assets are the assets that are “referenced or used by” Primary Assets and that will be brought along as you load the related Primary Assets.

  • By default, only Maps are Primary Assets.

  • If you want to have more types of Primary Assets, you have to** implement the GetPrimaryAssetId() function for their class in C++.**

  • The only exception to this rule is for Dynamic Assets, which are Primary Assets created at runtime. For those, you provide the PrimaryAssetId when you register them in the Asset Manager, so you don’t need to override GetPrimaryAssetId(), because that’s not where it will get the id from anyway.

  • There is this notion of “Asset Bundles” that pops up in the documentation from time to time, which is in my opinion poorly explained. It seems that asset bundles are another way of loading a bunch of assets (the first way being loading one or more Primary Assets). You tag **UPROPERTIES **as (meta = (AssetBundles = “Something”)) to include them in a bundle, which you can then load.

  • You don’t load bundles directly, you give the asset manager a list of currently loaded bundle names, a list of all the bundle names that you intend to have loaded (new and remaining) in the near future, and it spits out a list of the Primary Assets that you need to load. It’s kind of the delta between the two if you wish.

What I don’t fully understand:

  • Bundles seem to be an alternative implementation of Primary Assets, as they are in essence also an entity that references a bunch of Secondary Assets (but somehow you also need to associate a bundle with a primary asset?).
  • The documentation even goes as far as to title a paragraph with a title involving “Primary Assets”, then switches mid paragraph to talking about Asset Bundles, which is very confusing and happens multiple times. So either they are interchangeable, or they are meant to be used together / cover the same use cases.
  • Some functions take a list of primary assets and a list of bundles in order to load / unload them (also for the creation of a Dynamic Asset), and apparently some of those properties (the list of primary assets or the list of bundles) are optional, but it’s not clearly explained.

What I either don’t understand properly or is a bad implementation:

  • The way the asset manager works is different in the editor and in packaged mode (and I’m not talking about cooking issues). On one hand this is great, as any differences in how to manage assets are handled for you. On the other hand, you pretty much have to package your game every time you change something, because you can’t actually be sure that what you see in the editor will still work. This is somewhat the case for all aspects of game development, but the issues seem to occur way more often here.
  • The Asset Manager seems to have been created with a niche use in mind (The post linking the pre-release PDF only mentions it being useful for people building “a complex game with RPG elements”), even though it’s now presented as being more broadly useful.
  • However, this usefulness is all relative, as it seems like you basically have to edit all your UPROPERTIES to include a bundle name, and/or find a way to reference all your assets from one or more types of Primary Assets.
  • Artists and designers can apparently reference assets from a Primary Asset directly from the editor, which means that you have to manually enter them… one by one?? You can also implement the Primary Asset logic directly into an existing actor, etc… But then you need to revamp all your actors types, manage references, bundles, etc… Not much better.
  • If your game is very static, with a bunch of assets that needs to be loaded at predictable times and in cleanly delimited predictable groups, then the Asset Manager seems to have been made for you. Otherwise… Not so much.

What I want:
I initially planned to have an Asset Management system that would simply receive an FName containing the name of the asset that I want to load, using the type-appropriate function (GetMaterial, GetTexture, etc…). I already have an implementation of that using ObjectLibraries, which at game launch scans specific folders for specific classes, and keeps maps of <FName, FAssetData> at hand, in order to provide or load assets as needed.

**Example: **


USoundCue* SoundCue = RessourcesManager->GetSound("UI_Click_03"); // Simple as that.

This is a synchronous call so either the assets are small enough that it doesn’t matter, or I have pre-loaded them beforehand to avoid hitches.

The issue with that is that apparently it works very poorly in the editor, as I’m having a hard time to find a system that works in PIE, out of PIE, and that keeps working in between level changes (or all of that in any order).

Using the Asset Manager to do that felt possible for about five minutes, until I realized that** UTexture2D, USoundCue, etc… Do not have an implementation of GetPrimaryAssetId()**, and so will not work as a Primary Asset. This is a shame because otherwise the asset manager would nearly be perfect for me out of the box.

Instead, I have tried to scan my folders with an object library, and create Dynamic Assets at runtime when the game starts. This may sound silly as Dynamic Assets are supposed to not have a disc representation, but this way I can specify their PrimaryAssetId without having to change the engine code for lots of base asset classes.
This works great in the editor, but in packaged game nothing loads. Or rather, between the time where the Asset Manager loads the assets, and the time where I start using them, all the FAssetData are no longer valid. (This is not a cooking issue as my .pak file size hasn’t changed since I’ve started using the asset manager)

At first it felt like my needs were being very reasonable and very common (I just want a way to load an asset on demand by giving the asset manager the asset’s name), but after seeing the way the Asset Manager is implemented, I’m starting to doubt my judgment. So:

  • Is there a standard and battle-hardened way of handling the loading and unloading of assets in games?
  • How are YOU doing the asset management in your game?
  • Surely this problem must be common accross all platforms, game types, etc… Unless most games made around here do not have a RAM footprint worth reducing?

Let’s discuss!

Thank you for your attention,
Altrue

2 Likes

While you’re developing your game, resources are changing constantly: new meshes, modified textures etc. And those resources come from different applications with different file formats.
The problem in Unreal4 is its need of using proprietary .uasset files for any external resource.
I consider this a tragic design flaw.

A better asset system would allow the direct use of external resources. For example you would link per path, as you’ve shown above, to an FBX file or a PNG texture.
Then the system would dynamically create a cached proxy, the actual .uasset file for you without interaction and use this until you change the associated original resource.

I’m using a system where my asset manager points to asset files written in json format. These asset files contain only the meta information of the actual binary resource, mainly the path, a friendly name etc.

This would also make it much easier to create text-based assets that can be merged. The “asset” file would only contain settings for how to handle the resource, and you can add the .png/.fbx/whatever files to the repo separately.

Interesting, so you’re creating dynamic primary assets? Or is the term “asset manager” refering to the concept in general and not the 4.17’s novelty?

No, not the 4.17s, it’s my own asset manager.

Dont’t agree.
All those filetypes are only exchange formats, unreal need to use proprietary file formats to add extra data to implement the features you fine in the editor, moreover a proprietary file format can be implemented to be faster to be loaded/unloaded in memory at runtime.
An example is LODs: fbx doesn’t have the concept of lods, it can just contain more than one mesh, but it is unaware that those meshes represent the same asset and need to be use at different distances/screen size, lods are an game engine feature, so it is easier to have a single mesh asset with the extra data for lods than have a single fbx asset with tree mesh in it.

Do you really think that houdini work on with fbx internally? No, it uses custom data to represent vertices, meshes etc until you export, and the same do photoshop for png/jpg/etc, otherwise how could you add/manipulate/remove all the data you want to vertices at your choice?
E.g. you can use values from -1 to 1 or from 1 to 2 for vertex color in houdini, but once exported to fbx the exporter (as it is a proprietary file format) wil clamp everything in 0-1 discarting everythin lower than 0, and giving strange artifacts with values greater than 1.

This is the same for material asset, every game/render engine has its own implementation with pros and cons, but since there is no exchange file format for this kind of asset to pass a parquet material form a content creation application to a game engine no one ever think/complain about it.

I’m no expert of game engine implementation, but the book Game Engine Architecture give a clear overview of how those extremely complicated software are implemented and the tradeoff that are to be made to give the features that you use.

@Altrue - Did you reach any further conclusions about your original questions?

Alright, here’s the report of my progress so far regarding this topic :slight_smile:

I’ve currently settled for using custom-made primary assets (classes derived from UPrimaryDataAsset). I then instanciate these assets in the editor, and assign the UPROPERTYs they contain manually.

These assets are for the most part preloaded upon starting the game (since most can be useful at any point during gameplay).

Due to the way my game is designed, these Primary Assets are not representative of in-game entities (i.e no “Spell Asset” with particles, materials, sounds, etc…). Instead, these assets hold categories of the same (or similar) types. For instance, I have one for AsteroidMaterials, one for AsteroidMeshes, one for ShipMaterials, etc… My game being built with a lot of procedural generation in mind, I don’t intend to have a lot of static entities for which materials, meshes, etc… are set in stone at runtime.

(This is probably the biggest drawback of the Asset Manager, as its paradigm doesn’t really do well with dynamic entities. Continuing a trend that the engine in general -for all its qualities- exhibits, which is that as soon as the actors in your level aren’t fully static, you’re suddenly excluded from a lot of features.)

At this point, I should mention that all assets that are preloaded on game start, only ever have ONE instance created in the editor. This enables me to have my asset manager scan through my primary assets and store a reference to each primary asset in a separate variable, based on their (therefore unique) primary asset type.

My original post outlined my intent to use strings to quickly acquire the desired resources when needed in the code, but I’ve since then dropped the idea. Using strings seems inefficient and doesn’t enforce strict checking. Instead, since most of my primary assets are unique, I can do something like “AssetManager->GetShipMaterialAsset()->ShieldMaterial” to get my shield material. It requires more initial setup, manually assigning secondary asset references to the primary assets in the editor, one more include in the cpp file, etc… But at least once it’s done, there’s no more room for string references pointing to nothing without the IDE warning you.

Using this system I’ve been able to create a loading bar for my game that progresses as the preloading loops through each asset, which is mostly useless but I feel is more respectful for the player sitting at their computer waiting.

This method handles repeated PIE instances perfectly, and automatically preloads the assets once and for all on editor startup, not once every time a PIE instance starts.

The only downsides as far as I’m concerned are:
1- The method mentionned above relies on overloading the AssetManager function “PostInitialAssetScan” to automatically start preloading as soon as the assets are known, in editor and in packaged mode. It has the side effect of NOT preloading the assets when the editor still has the loading splash up. Instead, upon starting the editor you have to wait for the assets to finish preloading. Worse, if the editor doesn’t have the focus, for some reason the preloading takes forever to start.
2- More importantly, you cannot use the asset manager in constructors carelessly. You have to check for “if (!HasAnyFlags(RF_ClassDefaultObject)) {” before using the asset manager. So no replacing ConstructorHelpers::FObjectFinder yet!

I hope that this answers all your questions, and as always everyone is welcome to discuss their methods of asset management and asset loading!

Thank you for reading!

EDIT:

Because I wanted to keep using C++ parent classes with Blueprint child classes, without having to worry about my asset manager getting in the way (except for that flag check in the C++ constructor), I’ve modified the engine source to make the AActor function “ExecuteConstruction” virtual. (https://github.com/EpicGames/UnrealEngine/pull/4990)

This change enables me to create a child class of AActor which first checks if the asset manager is done preloading, before allowing the blueprint construction scripts to start (it subscribes to an event in case the asset manager is still working so that the construction scripts are triggered as soon as possible).

I’ve ended up not using C++/BP hybrids that much so far, but it’s very useful when prototyping. (Once the prototyping is done, the blueprint logic is ported in C++)

Thanks @Altrue,

This is very interesting. Although our current game is quite different in its asset usage pattern, getting any grasp on how the AM was intended to be used is proving problematic (and it seems to be one area where nobody is motivated to make any tutorials – probably because the real problems it might address just don’t happen in small example games :slight_smile: ). So any detailed example is interesting!

I am thinking for our case (very preliminary) that we’ll have a couple of cases:

  1. always loaded
  2. levels

And it doesn’t seem too hard to arrange the AM to give us that. There’s a bit of a question in my head about whether the first case might prove too large, in which case there might also be a:

1b) loaded on demand

(there’s some stuff only required when the player does particular customisations, and those could all be left unloaded until required…)

The bigger questions for me, however, are all around the question of best practices when authoring and how those will affect the approach to cooking, and I haven’t reached any strong conclusions about that…

Thanks again!

Ian

In the “Asset Manager” section of your project settings, where you would indicate which folder to scan for which primary asset types (including levels which are a primary asset in themselves), you can setup a cooking rule for each primary asset type.

So as long as your assets are either referenced by a primary asset with appropriate cooking rules, or referenced by an actor inside a level (putting something inside a level technically counts as “referenced by a primary asset”) which you package, you should have no problem with cooking :slight_smile:

I’ve recently upgrade to 4.21.2, and not all of the sudden my packaged game spews out thousands of lines about how it’s ignoring primary assets because they don’t have a valid primary asset ID. The line looks like this:


[2020.01.14-22.26.21:327]  0]LogAssetManager: Warning: Ignoring primary asset Tea_Bag_EarlGrey_BP - PrimaryAssetType InventorySystem - invalid primary asset ID

It doesn’t *seem *to be breaking anything, but is very annoying.
I’ve checked in the “Audit Assets” window and indeed, these things don’t have an ID (despite me calling them out in the Asset Manager settings).

What can I do?

Turns out I needed to create PrimaryAssetLabels for everything (although that still didn’t get rid of every warning).

Now, I’m having trouble where things are being duplicated into multiple chunks! I really wish there would be better documentation articles on this system and how to actually use it. I’m muddying my way through this apparently super-complicated process and falling into every pitfall it seems…