AssetBundles can become dependent on other AssetBundles if one or more of the UnityEngine.Objects
contains a reference to a UnityEngine.Object
located in another bundle. A dependency does not occur if the UnityEngine.Object
contains a reference to a UnityEngine.Object
that is not contained in any AssetBundle. In this case, a copy of the object that the bundle would be dependent on is copied into the bundle when you build the AssetBundles. If multiple objects in multiple bundles contain a reference to the same object that isn’t assigned to a bundle, every bundle that would have a dependency on that object will make its own copy of the object and package it into the built AssetBundle.
Should an AssetBundle contain a dependency, it is important that the bundles that contain those dependencies are loaded before the object you’re attempting to instantiate is loaded. Unity will not attempt to automatically load dependencies.
Consider the following example, a Material in Bundle 1 references a Texture in Bundle 2:
In this example, before loading the Material from Bundle 1, you would need to load Bundle 2 into memory. It does not matter which order you load Bundle 1 and Bundle 2, the important takeaway is that Bundle 2 is loaded before loading the Material from Bundle 1. In the next section, we’ll discuss how you can use the AssetBundleManifest
objects we touched on in the previous section to determine, and load, dependencies at runtime.
This section describes how Unity deals with duplicated information across AssetBundles. The scenario discuessed in Editor Setup is still applicable to the Unity Addressable Asset System (‘Addressables’). The technique described in Runtime loading is automatically performed in the background by the Addressables system.
Unity doesn’t minimize the amount of duplicated information across AssetBundles by default. Unless explicitly stated, or a dependency graph is established, Unity also includes copies of any implicitly referenced assets.
For example, you could have an application with two prefabsAn asset type that allows you to store a GameObject complete with components and properties. The prefab acts as a template from which you can create new object instances in the scene. More info
See in Glossary NuclearElectricityGenerator-Medium
and ProductionCrystals-Large
, and each one is assigned to its own AssetBundle.
The image below shows an asset with an AssetBundle.
Both prefabs share the same SpaceStationModules-Material-Active
material, which is not assigned to an AssetBundle. This material references the SpaceStationModules-Material-Active
texture, which is not assigned to an AssetBundle either.
The image below shows an asset without an AssetBundle.
If you build the AssetBundles using the CreateAssetBundles
class described in AssetBundle Workflow, each generated AssetBundle contains the material (including its shader and referenced textures). In the example image below, the prefabs have a size of 383 KB and 377 KB respectively.
If the project contains a larger number of prefabs, this behaviour impacts the final installer size, and also runtime memory footprint when both AssetBundles are loaded. Data duplication across AssetBundles also impacts batching, because Unity considers the various copies of the same material as unique materials.
To avoid data duplication, assign the material and its referenced assets to its own modulesmaterials
AssetBundle. You can also tag the material only, because the texture dependency is also included in the AssetBundle during the build process.
The image below shows an asset with the modulesmaterials
AssetBundle added.
Now if you rebuild the AssetBundles, the generated output includes a separate “modulesmaterials” AssetBundle (359 KB), which contains the material and its associated texture. This significantly reduces the size of the other AssetBundles for the prefabs (from roughly 380 KB in the previous iteration down to roughly 20 KB).
The image below illustrates this.
When you extract common assets to a single AssetBundle, be careful about dependencies. In particular, if you try to instantiate a module by loading its prefab only, using a variation of the example in Using AssetBundles Natively, the materials do not load:
using System.IO;
using UnityEngine;
public class InstantiateAssetBundles : MonoBehaviour
{
void Start()
{
var moduleAB = AssetBundle.LoadFromFile(Path.Combine(Application.dataPath, Path.Combine("AssetBundles", "nuclearelectricitygenerator-medium")));
if (moduleAB == null)
{
Debug.Log("Failed to load AssetBundle!");
return;
}
var prefab = moduleAB.LoadAsset<GameObject>("nuclearelectricitygenerator-medium");
Instantiate(prefab);
}
}
The image below shows an example where the material has not loaded.
To solve this problem, load the materials AssetBundle in memory before you load the AssetBundle that belongs to the module. To do this, add the following statement at the top of the Start() function:
var materialsAB = AssetBundle.LoadFromFile(Path.Combine(Application.dataPath, Path.Combine("AssetBundles", "modulesmaterials")));
The image below shows an example where the material has loaded correctly, using the method above.
Note: When using LZ4 compressed and uncompressed AssetBundles, the AssetBundle.LoadFromFile method only loads the catalog of its content in memory, but not the content itself. To confirm this behaviour use the Memory Profiler package.