Hi everyone,
This article is based on the Unite 2024 talk Making live updates across platforms with Addressables by Attilio Carotenuto, Senior Technical Lead at Unity. This post covers a few tips on how to leverage Addressables for creating and managing your game content and is intended for those who are already familiar with Addressables.
Addressables, and Asset Bundles, are a powerful way to structure your game in logical blocks that can then be exported separately and added to the main executable whenever needed. They are used to load and unload assets, to configure, build, and load asset bundles that you can then distribute to your players on demand. The Addressables system is built on top of Asset Bundles, taking care of dependencies resolution and bundle loading for you.
If you’re new to Addressables, make sure you check out the Get started page in Unity Documentation.
But first, we asked Attilio what are some of the most common questions he gets asked by customers when it comes to using Addressables.
Q: How do I get started with Addressables?
A: When starting a new project, I recommend leveraging Addressables from the start and ensuring that every new asset is registered as an Addressable.
For existing projects that already have a large amount of assets, their own loading logic for scenes and assets, and now intend to switch to the Addressables system, the initial setup can be challenging. I often see studios taking an incremental approach where only a portion of assets are moved over to Addressables, while scenes and other assets are not marked as such. This is not an ideal setup.
My recommendation in either case is to go all-in and ensure everything that can be marked as Addressables is added to the system. A hybrid approach is rarely effective, and usually leads to confusion, duplicated assets in runtime memory, as well as duplicated content between the game build and the asset bundle. This affects game performance, install time, and build size.
Q: How should I group assets together?
A: Aim to group assets based on how often they are loaded and used together. Forming groups is a similar process to packing sprite sheets, where you want to minimize the amount of sheets loaded in memory at the same time.
I often see projects grouping their assets by type, with groups like Materials, UI, Prefabs and so on. This is not ideal. Instead, you should analyze how players progress through your game and create, for example, an Intro Group, a Tutorial Group, Level1, and so on, with common and shared dependencies in their own separate groups. This will improve runtime memory usage, reduce boot time, and as a result improve game retention as well, since players won’t have to stare at a black screen or a loading image before playing.
Addressables Scenes will always end up in their own bundle, even if you group them together with other regular assets. These are called Streaming Scene Asset Bundles. For this reason, it’s best practice to put them into their own group, so it’s easier to visualize what bundles are being generated.
When in doubt, split the assets into smaller groups. The footprint of multiple smaller bundles is reduced now, compared to older versions of Unity, as I will cover shortly.
Q: Is it better to have a few large bundles, or many small ones?
A: In my experience, small bundles are often the best choice, for many reasons. We have seen large projects gaining a very significant bump in performance, and reduction in boot time, just by breaking up their groups into smaller ones.
Working with smaller bundles normally leads to shorter dependency chains. Also, due to how Unity handles loading and unloading assets within bundles, smaller bundles normally lead to lower runtime memory usage.
Many developers tend to prefer large bundles, due to older versions of Unity allocating separate caches for each bundle. This is no longer the case, as we will cover later in the article.
Read on to learn more about how to work with Addressables.
Advanced content management techniques
In this section, we’re going to share a few tips on how to effectively group your game assets into bundles, pitfalls to look out for, and how to improve the performance of your game.
Managing dependencies
When you load an asset bundle at runtime, all the dependencies of that bundle will also need to be loaded. It’s fairly common to end up with unclear dependency trees, where a single asset will cause hundreds of downloads.
Imagine someone trying to load the IntroScreen prefab, which is actually part of a UI group (that contains the menu, buttons, and other UI-related assets). That means that all the dependencies of that UI group will also need to be loaded at the same time, even though they aren’t needed yet.
Another example could be a game with a final boss. This final boss uses a custom shader and it’s all nicely packed – model, animation, material – in one bundle. Players go through your game, they get to the end, and then the final boss bundle is loaded. Everything works well in this scenario.
Now, let’s say that the content team authored a new enemy in the first level of the game and reuses the custom shader from the final boss. The team now created a dependency from the start of the game to the final boss asset bundle.
This means that when players start the game, they have to load end game content. And, if the final boss asset bundle has other dependencies, then that creates an even longer dependency chain. In this case, the solution is to move this specific shader into its own bundle, in order to break the dependency chain.
Analyzing Asset dependencies
To get an overview of the dependencies of an asset, you can right-click on it in the Project tab, and click on Select Dependencies. Unity will show the amount of dependencies in the Inspector, and it will also apply a filter in the Project tab to show what those dependencies are.
Addressables Asset dependencies in the Unity Inspector
At runtime, you can use the Addressables Profiler Module to track what assets are being loaded as you play the game, along with the related asset bundles and dependencies. When you select a frame, it will show information in a tree view regarding loaded assets, asset bundles, groups and so on. If you’re using an older version of the Addressables package, you should use the Addressables Event Viewer instead.
The Addressables Profiler Module
One powerful way to reduce dependencies is to leverage AssetReferences while writing your scripts. These are essentially soft references that allow you to control when an asset is loaded and unloaded, breaking the direct dependency chain. They require more work due to memory management (which can lead to memory leaks), but they will massively reduce your game loading times and runtime memory usage if used properly.
Analyzing your Addressables Build
When working with Addressables, it’s essential to be able to quickly understand what asset bundles you’re generating, what assets they contain, eventual duplicates, and why it is taking so long to build.
We can do all of that using Unity’s Build Layout Report.
By default the report is disabled, as generating it will cause an increase in build time. You can enable it via Preferences > Addressables > Debug Build Layout as shown in the screenshot below.
The Debug Build Layout option in the Addressables tab
Next time you’ll make a build, Unity will generate a report with some essential information, such as platform, build duration, and number of bundles.
Here’s an example of a report:
An example of an Addressables Report summary
The first tab, Summary, will be populated with general data and stats regarding your build.
In the Aggregate Information section you can see how many bundles and assets are included in your Addressables build.
You might want to take a closer look at the Assets pulled into a build by an Addressable section. These are non-Addressable assets being included in bundles due to being dependencies to other Addressable assets. This will cause an overall increase in build size, as assets end up being duplicated between multiple asset bundles and the actual game build. It can also lead to increased runtime memory usage, as duplicates end up being loaded in memory unnecessarily.
The Explore tab in the Addressables Report window
The Explore tab allows you to analyze your game assets with multiple views.
AssetBundles will show a list of all your Asset Bundles, as well as what assets are included. It’s very handy to understand your bundle composition and detect asset duplicates by using the References To/By view on the lower-right part of the window.
Refs To and Refs By in the Explore tab, part of the Addressables Report window
Assets will show a raw list of all your game assets, their size, as well as references in both directions. Groups will show your Addressables groups, and within each of those the resulting asset bundles, and the assets contained in each bundle.
The View By Groups view in the Explore tab, in the Addressables Report window
Labels will show the labels you defined within your Addressables setups, along with the assets that are tagged with those labels you’ve set up.
Finally, in the Potential Issues tab, you will find a quick overview of duplicated assets, indicating how much space could be saved by fixing those occurrences.
The Potential Issues tab in the Addressables Report window
Mixing Addressable and non-Addressable assets
Whenever an Addressable asset directly references a non-Addressables asset, it will need to bring a copy of that asset into its respective bundle.
For example, let’s say that there are two prefabs, part of different groups and different bundles, using the same non-Addressable material. They will each bring a copy of this material into their own bundle, because it’s not an Addressable. This leads to increased build size as the bundles will be larger, due to this duplicate material. There will also be an increase in runtime memory usage because of the duplication.
Similarly, if you have a non-Addressable scene using Addressables assets, those assets will be copied over in the game build, resulting again in increased size on disk and runtime memory usage.
These cases are particularly risky when dealing with centralized data repositories, as it can lead to tricky bugs where you now have two copies of that asset in memory, when you’ve only expected one.
You can use the Build Layout report to help investigate and fix these occurrences, as shown in the previous section.
As mentioned earlier, when you move to Addressables, you should make sure to move everything you can to the Addressables system. If you opt for a hybrid approach, where you move only some of them to Addressables, it can have a lot of pitfalls.
For scenes, the right approach is to have a lightweight non-Addressable bootstrap scene, responsible for loading your initial Addressable scene, and then ensure all the other scenes are marked as Addressables.
Loading and unloading assets
Whenever you attempt to use an asset contained within a bundle, Unity ensures the corresponding bundle is loaded into memory, then in turn loads the asset into memory. While it’s possible to partially load specific assets within an Asset Bundle, the opposite isn’t allowed. This means that as soon as an asset within an Asset Bundle is loaded, it can only be unloaded if the entire group of assets is no longer needed.
As a result, if your bundle structure isn’t ideal, you’ll often see increasing runtime memory usage as the game goes on, leading to poor performance and potential out-of-memory (OOM) crashes.
For example, imagine that all your environments are contained within a single bundle. You play Level 1, and you load the relevant environment prefabs. When you finish the level, and move to Level 2, you load the Level 2 environment prefabs and the related dependencies. However, because they’re part of the same bundle, the Level 1 prefabs are still kept in memory. As you keep going through Level 3 and Level 4, you notice runtime memory usage increasing. Eventually you run out of memory and the game crashes.
This is why it’s best to avoid bundles with a large number of assets, as it will end up taking a lot of runtime memory and turn into a bottleneck for your game. As mentioned before, aim to pack assets based on how frequently they’re going to be loaded and used together; when in doubt, split them into smaller bundles.
Internal Asset Bundle data
Whenever you want to load an asset, Unity first needs to load the AssetBundle in memory. Each Asset Bundle contains, along with game assets, extra information and headers that are used by the Unity Runtime to know which assets to load and how.
The first piece of data is the Table of Contents. This is a map of the assets contained within the bundle, allowing the Runtime to look up and load individual assets by name.
Then comes the Preload Table. This lists the dependencies of each asset contained within your bundle, and it’s used by Unity to correctly load and construct assets. The size in memory for this one is usually not a concern, unless you have very long dependency chains. Then it might become something that you need to have a closer look at.
Unity stores the Engine Version used to build the Asset Bundle both in the bundle header, and in the headers of the serialized files within the bundle. This can lead to cases where bundles are determined to have changed, even though the actual assets included are identical. You can use the ContentBuildFlags.StripUnityVersion when building your bundles to strip the version which will instead be stored as 0.0.0.
Finally, we have TypeTrees, which define the serialized layout of the objects contained in the AssetBundles.
They are necessary to maintain compatibility when upgrading the Unity version of your game build. If Unity detects a mismatch between the definition of an object in the AssetBundle, and in the game build, Unity can use TypeTrees to do a Safe Binary Read and attempt to load it anyway. However, this has a performance cost, so it’s recommended to update the bundles whenever you update the Unity version.
The size of the TypeTrees depends on how many different types of objects are contained within the bundle. Each bundle has its own TypeTree, so having multiple bundles containing the same type of objects will slightly increase the total size on disk. On the other hand, when loaded, TypeTrees are stored in a global cache in memory, so you won’t incur a higher runtime memory cost if multiple asset bundles are storing the same type of objects.
TypeTrees can optionally be disabled, by setting the ContentBuildFlags.DisableWriteTypeTree flag when building your bundles. This will make your bundles and the related memory overhead slightly smaller, but that also means that you need to rebuild all your bundles whenever you update the Editor version of your game build. This can be especially painful if you rely on bundles built from your players for user-generated content. Unless you have a very strong reason to do so, it’s recommended that you keep TypeTrees enabled.
One use case where it’s safe to strip TypeTrees is for local bundles, as they are included as part of the build and will be rebuilt whenever you update the game or the Editor version.
Loading cache
The Loading cache is a shared pool of pages where Unity stores recently accessed data for your AssetBundles. This is global, so it’s shared between all the AssetBundles within your game.
This was introduced in Unity 2021.3 and backported to Unity 2019.4. Previously, Unity relied on separate caches for each AssetBundle, called Serialized File Buffers. For this reason, if your game had a lot of small bundles, you incurred higher runtime memory usage because each bundle had its own buffer. Since this is no longer the case, the footprint of smaller bundles is not as relevant anymore.
By default this is set to 1MB, but it can be changed by setting AssetBundle.memoryBudgetKB. The default cache size should be enough for most games, but there are cases where if your game has bundles with very small objects, and you increase the size, you might have more cache hits and that improves general performance.
Cyclic Redundancy Checks (CRC)
Whenever bundles are loaded, you have the option to run Cyclic Redundancy Checks (CRC) checks to ensure the content delivered to your game is intact and exactly what you expect.
CRC checks are used to do a checksum validation of your Asset Bundle. This validation is calculated based on the uncompressed content of your bundle, so it doesn’t include all the bundle headers (mentioned in the Internal asset bundle data section above and the compression type.
While convenient, this option can have a significant impact on your game’s performance, especially on mobile and console platforms. This is particularly relevant if CRC checks are being repeated for each AssetBundle load.
The Asset Bundle CRC option
As you can see in the image above, there are three options: Disabled, Enabled, Including Cached, and Enabled, Excluding Cached. Each Addressables group has its own setting.
Here are a few per-platform recommendations:
-
On consoles: AssetBundles are usually included as part of the game installation on local storage or downloaded as “downloadable content” (DLCs). In those cases, CRC checks will be unnecessary. Because of the performance cost on consoles, it’s best to keep the Disabled option checked.
-
On WebGL: All bundles are remote, and caching is disabled, as WebGL uses its own built-in download cache. In this case, CRCs should be left on Enabled, Including Cached.
-
On PC or mobile devices: It’s important to do CRC checks for remote bundles that are downloaded from a CDN. This is to prevent your game from crashing in case the data received is corrupted, truncated, and to prevent potential tampering and data mismatch. For this reason, you should choose Enabled, Excluding Cached. Unity will run CRC checks once for non-cached bundles, and as soon as they’re in the game, they’ll be treated as local bundles. For groups that are kept locally within the game build, you can pick the Disabled option.
Advanced content-loading techniques
Profile variables
While defining your content delivery strategy, you have the possibility of defining Build and Load paths within the Addressables Profiles, and in the individual groups. These inform the Editor where to build the AssetBundles and where to load them from at runtime. In order to support more complex setups and behaviours, you can use Profile Variables. They allow you to define parameters in your paths that are resolved either at build time or runtime.
The BuildPath and LoadPath options in the Addressables Profiles window
There are two types of Profile Variables:
- Brackets [ ] : These are resolved at build time and the resulting values are written directly into your catalog. They can’t be tweaked at runtime.
- Braces { } : These are resolved at runtime, when the Addressables system is initialized.
You can use static fields and properties for both types, with fully qualified names, which are then evaluated using reflection. For runtime variables, make sure those are set before you load the first asset, or before you initialize the Addressables system, because they are only evaluated once.
Some variables are built-in, such as BuildTarget, which is needed because AssetBundles aren’t inherently cross-platform and each platform needs its own set of AssetBundles.
You can also define your own Profile Variables, as shown in the example where the first part of the Load URL is a runtime parameter. This allows you to create redirects, so on a development build you can load content from a local or development storage, and on a production build it can point to your production storage account. When you’re developing a live game, it’s common to have a single build that is used for different environments, such as development, staging, and production.
A similar approach can be used for other settings such as live seasons. Imagine you want to load different textures and assets based on the active live season. You can define the live season, with fully qualified name, in braces, and as soon as you set it at runtime when the game starts, it will load a different bundle showing a different background, a different main menu, etc. You won’t have to make any changes to the game build or distribute a version update to your players.
Make sure that the Editor doesn’t strip these variables, as they’re evaluated using reflection, so you need to make sure they’re referenced somewhere.
TransformInternalId
This function allows you to introduce additional logic that will be executed whenever Unity evaluates Resource Locations at runtime, in order to load assets or asset bundles.
You can use it for example to transform the URL when doing remote fetching, to do redirects or adding extra parameters such as secret keys. You can also use it to load specific versions of your assets, such as high/low-res textures, or even specific Addressables catalogs.
For example, let’s say you’re working on a mobile game: You can check the capabilities of the device, such as the size and density of the screen. If it’s a low-resolution screen, you load low-resolution textures, while if it’s a tablet, you can load high-resolution textures.
Here’s an example of appending a secret key to a Remote Asset Bundle URL:
private const string SECRET_KEY = "very_secret_key";
public void Init()
{
Addressables.InternalIdTransformFunc = TransformSetSecretKey;
}
private string TransformSetSecretKey(IResourceLocation location)
{
if (location.ResourceType == typeof(IAssetBundleResource) &&
location.InternalId.StartsWith("https"))
{
return $"{location.InternalId}?key={SECRET_KEY}";
}
return location.InternalId;
}
Appending secret key to Remote Asset Bundle Load URL
As you can see, we’re first checking the resource type Unity is trying to load. If it’s an Asset Bundle, and the Internal Id (which in this case is the URL) starts with “https”, we determined Unity is trying to download an Asset Bundle from the CDN, so we append the extra parameter.
Make sure to register the callback before you load any assets, otherwise you’ll miss some of these resource location resolutions.
More resources
- Addressables in the Unity Manual
- Making live updates across platforms with Addressables | Unite 2024
Thanks for reading! We hope you find this article useful.








