0% found this document useful (0 votes)
3 views63 pages

External - AssetBundles FAQ

This document provides a comprehensive FAQ on AssetBundles in Unity, covering their creation, compatibility, and loading processes. It addresses common issues, performance considerations, and best practices for managing AssetBundles, including their relationship with Addressables and the Scriptable Build Pipeline. The content is aimed at developers looking to understand and effectively utilize AssetBundles within their Unity projects.

Uploaded by

engin_e
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views63 pages

External - AssetBundles FAQ

This document provides a comprehensive FAQ on AssetBundles in Unity, covering their creation, compatibility, and loading processes. It addresses common issues, performance considerations, and best practices for managing AssetBundles, including their relationship with Addressables and the Scriptable Build Pipeline. The content is aimed at developers looking to understand and effectively utilize AssetBundles within their Unity projects.

Uploaded by

engin_e
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

‭AssetBundles FAQ‬

‭ ote: Comments and fixes are welcome. Please help point out any incorrect information, or to‬
N
‭suggest further topics to cover‬

‭Introduction‬ ‭‬
3
‭What is the scope of this document?‬ ‭3‬
‭General Questions‬ ‭4‬
‭What is a Scene AssetBundle?‬ ‭4‬
‭How do I create an AssetBundle?‬ ‭4‬
‭Can you build AssetBundles individually, then use them together at runtime?‬ ‭4‬
‭Can I load AssetBundles built on an older version of Unity?‬ ‭4‬
‭Can I load AssetBundles built on a newer version of Unity?‬ ‭5‬
‭What happens if an AssetBundle is out of date?‬ ‭6‬
‭Some users of my game are having crashes with AssetBundles, what is going on?‬ ‭6‬
‭How do I ship AssetBundles with my Player build?‬ ‭7‬
‭How do Prefabs work in AssetBundles?‬ ‭7‬
‭How do Scenes share assets in AssetBundles?‬ ‭8‬
‭How can I log the time taken during an AssetBundle build?‬ ‭10‬
‭Do AssetBundles have a file extension?‬ ‭11‬
‭How does Unity treat uppercase letters in an AssetBundle name?‬ ‭11‬
‭Why are my files reimporting or builds different between Windows and Mac?‬ ‭12‬
‭Are AssetBundle Builds Deterministic?‬ ‭12‬
‭Tips and Tricks‬ ‭14‬
‭Should AppendHashToAssetBundleName be used?‬ ‭14‬
‭How can I build AssetBundles with different compression settings?‬ ‭14‬
‭How can I perform partial builds and still use the inspector to assign Assets to‬
‭AssetBundles?‬ ‭ 5‬
1
‭Are there issues with Animation Override Controllers and AssetBundles?‬ ‭16‬
‭Are there issues with Scriptable Render Pipelines and AssetBundles?‬ ‭16‬
‭Inside AssetBundles‬ ‭17‬
‭What is the format of a Unity Archive file?‬ ‭17‬
‭What is inside an AssetBundle Archive?‬ ‭17‬
‭What is the naming convention for SerializedFiles inside an AssetBundle?‬ ‭19‬
‭What is inside an AssetBundle SerializedFile?‬ ‭20‬
‭What is a .ResS file?‬ ‭20‬
‭How do References To ResS Files work?‬ ‭21‬
‭What is a .Resource file?‬ ‭21‬
‭How can I see what is inside an AssetBundle?‬ ‭22‬
‭ ption 1: WebExtract + Binary2Text‬
O ‭22‬
‭Option 2: UnityDataTools‬ ‭23‬
‭Option 3: AssetBundles Browser‬ ‭23‬
‭Option 4: Via BuildReport‬ ‭23‬
‭How can I see what Unity Version was used to build an AssetBundle?‬ ‭24‬
‭How can I check the Compression for an AssetBundle‬ ‭24‬
‭What are the different “Manifests” generated by an AssetBundle Build?‬ ‭25‬
‭Do AssetBundle builds generate a BuildReport?‬ ‭30‬
‭Where can I find a breakdown of the size of data inside an AssetBundle‬ ‭31‬
‭CRC and Hash Topics‬ ‭32‬
‭How do CRCs work with AssetBundles?‬ ‭32‬
‭What is the AssetBundle Hash generated by a build?‬ ‭32‬
‭How do Incremental Builds use the AssetBundle Hash?‬ ‭35‬
‭How is the AssetBundle Input Hash calculated?‬ ‭36‬
‭What is the TypeTreeHash?‬ ‭36‬
‭Downloading AssetBundles‬ ‭38‬
‭What built-in mechanism does Unity offer for downloading AssetBundles?‬ ‭38‬
‭What recompression happens to AssetBundles when entering the cache?‬ ‭38‬
‭Which signatures of [Link] support caching?‬ ‭39‬
‭What happens when [Link] is used without‬
‭caching?‬ ‭39‬
‭How does [Link] determine the bundle name for‬
‭the cache?‬ ‭40‬
‭What is the Hash used by the AssetBundle cache?‬ ‭40‬
‭How can I use the AssetBundle hash as a version hash when downloading an‬
‭AssetBundles?‬ ‭41‬
‭How can I generate my own version hash when downloading an AssetBundles?‬ ‭42‬
‭What is the structure of the Asset Bundle Cache?‬ ‭43‬
‭Loading Local AssetBundles‬ ‭44‬
‭What is the performance problem with LZMA compression and local AssetBundles?‬ ‭44‬
‭What does the Assert “PersistentManager: File …. Was expected to exist…” Mean?‬ ‭45‬
‭Performance‬ ‭46‬
‭What memory is used when loading Asset Bundles?‬ ‭46‬
‭When does Unity load the entire AssetBundle into memory?‬ ‭47‬
‭How can I reduce runtime memory usage in AssetBundles with many Assets?‬ ‭48‬
‭Platform Topics‬ ‭49‬
‭Can I load the same AssetBundles on different platforms?‬ ‭49‬
‭How are new AssetBundles downloaded to consoles?‬ ‭49‬
‭Should CRC checks be performed on Consoles?‬ ‭49‬
‭What platforms do not support AssetBundle caching?‬ ‭50‬
‭Related Technology and Other References‬ ‭51‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭1‬
‭ hat is the relationship between AssetBundles and Addressables?‬
W ‭ 1‬
5
‭What is the relationship between AssetBundles and the Scriptable Build Pipeline?‬ ‭51‬
‭Are ".unitypackage” files a type of AssetBundle?‬ ‭51‬
‭Where to get more information about AssetBundles?‬ ‭51‬
‭Advanced Topics‬ ‭52‬
‭Why is it recommended to use the BuildSettings UI to set the current target for an‬
‭AssetBundle build?‬ ‭ 2‬
5
‭What is the AssetBundle Object?‬ ‭53‬
‭How can I tell what objects can be loaded from an AssetBundle?‬ ‭54‬
‭What is a PreloadData object?‬ ‭56‬
‭How do references between AssetBundles show up inside a SerializedFile?‬ ‭58‬
‭How are script types represented inside AssetBundles?‬ ‭61‬
‭Appendix:‬ ‭62‬
‭How to read the header sample code‬ ‭62‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭2‬
‭Introduction‬

‭What is the scope of this document?‬


‭ his document covers frequently asked questions about AssetBundles. It focuses on‬
T
‭AssetBundles as generated by the functionality built-in to the Unity Editor (the‬
‭[Link] API). This is sometimes called the “native” AssetBundle‬
‭support.‬

‭ his document also has a lot of relevant content for Addressables and the Scriptable Build‬
T
‭Pipeline, because those packages also create and load the exact same Asset Bundle file‬
‭format. But this document does not attempt to cover all the details of those packages.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭3‬
‭General Questions‬

‭What is a Scene AssetBundle?‬


‭ “regular” AssetBundle is built based on a list of assets, e.g. Prefabs, Materials,‬
A
‭ScriptableObjects etc etc.‬

‭ nity also supports assigning Scene files to an AssetBundle. However you cannot mix Scenes‬
U
‭with other assets inside a single AssetBundle. In the API this type of bundle is called a‬
‭“streaming scene” AssetBundle, see‬‭[Link]‬

‭ he process of building scenes into AssetBundles is similar to what happens during a Player‬
T
‭build, in fact a lot of the same code is reused.‬

‭How do I create an AssetBundle?‬


‭ here is no built-in UI to trigger an AssetBundle build. Instead developers will write a small‬
T
‭script that is exposed as a menu and which calls‬‭[Link]‬‭.‬

‭ lternatively you can use the Addressables package, which has a user interface. That is‬
A
‭outside the scope of this document.‬

‭ an you build AssetBundles individually, then use them together‬


C
‭at runtime?‬
‭ enerally it is expected that you build all your assetbundles at the same time, so that Unity can‬
G
‭determine all the dependencies and avoid repeating assets that are already present in another‬
‭assetbundle.‬

I‭n practice, if you understand the dependencies between assets sufficiently, and have your‬
‭assets grouped into independent “islands”, then you do not need to build them all with a single‬
‭call.‬

‭Can I load AssetBundles built on an older version of Unity?‬


I‭n most cases, the engine supports loading AssetBundles from previous versions of Unity. The‬
‭file format for AssetBundles has been quite stable between versions and Unity can understand‬
‭and load files generated by older files. What is likely to be a problem is if the objects serialized‬
‭inside the AssetBundle are not compatible with the latest version of Unity.‬

I‭f the serialization format for an object has changed then AssetBundle load code will read that‬
‭object using the “safe binary read” deserialization method. This code path uses type trees to‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭4‬
f‭ind fields in the serialized data that match fields in the current object serialization layout. There‬
‭is some performance cost to doing this matching.‬

I‭f you built your AssetBundles without type trees‬


‭(‬‭[Link]‬‭), then‬‭any serialization changes in the newer‬
‭version of Unity would completely prevent a successful load (and potentially even lead to a‬
‭crash). This flag should only be used when the version of Unity used to load the assetbundles‬
‭can be guaranteed to match the exact version of Unity that generated the assetbundles.‬

‭ dditionally, we do not guarantee backward compatibility for all Objects and Unity features. We‬
A
‭attempt to maintain it, but individual teams might change their serialization code without‬
‭considering that it could cause a backward compatibility issue. It is also possible that individual‬
‭objects can load, but the expected structuring of the objects has changed, e.g. if expected‬
‭objects are completely missing, or the references between objects do not establish an expected‬
‭hierarchy. As the version gap between the building and loading of AssetBundles widens, it is‬
‭more likely that some objects will not load correctly.‬

‭Can I load AssetBundles built on a newer version of Unity?‬


‭ he ability to read AssetBundles generated by a‬‭newer‬‭version of Unity is less likely to be‬
T
‭successful, because “forward compatibility” is much harder to achieve than “backward‬
‭compatibility”. If the version gap between Unity versions is small then there may be no problem.‬
‭But as the version gap widens the risk of incompatibility increases. For example if the file format‬
‭of AssetBundles changes then it may simply not be readable by older versions. Or if an‬
‭Object’s serialization changes in a radical way, then it might not work in an older version of‬
‭Unity, even when safe binary read is used. This situation is not specific to Asset Bundles, the‬
‭same can be true for Scenes and Prefab and other assets.‬

‭ lso one specific case to mention. There was a change related to padding in the AssetBundle‬
A
‭header made around March 2022. This fix is backward compatible, but could mean that older‬
‭versions of Unity, prior to that backport landing, cannot load AssetBundles generated after that‬
‭change (e.g. not forward compatible).‬

‭ lso worth mentioning is that several Shader and Terrain related backward compatibility bug‬
A
‭reports and forum posts about shaders not working properly when loading AssetBundles‬
‭created in earlier versions of Unity.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭5‬
‭What happens if an AssetBundle is out of date?‬
‭ y default Unity includes the TypeTrees in the SerializedFiles that are built into an AssetBundle.‬
B
‭The TypeTrees take up some space but, by recording the serialization format of each object,‬
‭they make it possible to perform a “safe binary read” if there is a mismatch between the‬
‭definition of an object between the AssetBundle and the player build. For example if a‬
‭MonoBehaviour had a field removed and the player is rebuilt, then it would still be able to read‬
‭older instances of that MonoBehaviour that had the extra field from an older AssetBundle.‬

‭ he serialization definition of built-in objects can change when Unity is upgraded, and C# class‬
T
‭within a project may also change as the project evolves.‬

‭ he Safe Binary Read approach works for individual objects. If there are major changes in the‬
T
‭expected state of an object or structural changes in object hierarchies, then old serialized data‬
‭may not load properly. So it can still make sense to always rebuild AssetBundles along with‬
‭Player releases to keep them in sync.‬

‭ ome users of my game are having crashes with AssetBundles,‬


S
‭what is going on?‬
‭ common problem, especially for AssetBundles downloaded over the web, is that there can be‬
A
‭data corruption in the transfer. For example the file might be truncated. Or perhaps the wrong‬
‭version of a file gets distributed by accident.‬

‭ he fix for this is to make sure there is data verification happening as part of the assetbundle‬
T
‭distribution system. The assetbundle should be checked after download, or prior to load, and if‬
‭the content is not correct it can be erased and re-downloaded immediately.‬

‭ his can be done by calculating a CRC or content hash of the file, and checking it against‬
T
‭expected value. A customer might create their own system for tracking and publishing‬
‭expecting hash values. One complexity is that Unity will recompress AssetBundles when‬
‭UnityWebRequestAssetBundle‬ ‭and AssetBundle caching‬‭are used, so a hash of the file content‬
‭will be invalidated. For that reason the CRC support provided by Unity is recommended‬
‭because it is independent of the compression.‬

‭ ecause of the cost of checking CRC or content hashes it is best done once, as the file is‬
B
‭downloaded, rather than repeating it at every single load. See later section for more details‬
‭about hashing, CRC and download support.‬

‭ ithout some sort of validation layer then it is pretty much guaranteed that, sooner or later,‬
W
‭there will be corruption leading to random crashes, or bad behavior, that are quite difficult to‬
‭pinpoint.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭6‬
‭How do I ship AssetBundles with my Player build?‬
‭ ou can build AssetBundles into a folder named “StreamingAssets” in your project. Any files‬
Y
‭found in that location are automatically packaged as part of your next Player Build.‬

‭ hen at runtime you can load those AssetBundles using‬‭[Link]() or‬


T
‭LoadFromFileAsync()‬‭. The path to the StreamingAssets‬‭location in the player can be retrieved‬
‭from‬‭[Link].‬

‭How do Prefabs work in AssetBundles?‬


I‭n the Editor, Prefabs can be instantiated inside Scenes, or nested inside other Prefabs. Each‬
‭prefab instance can be customized with property modifications (for example setting the‬
‭transform). These are saved inside the hosting scene or prefab. But Unity Runtime does not‬
‭support prefabs.‬

‭So what does it mean to put a Prefab into an AssetBundle?‬

‭ hen a scene (or prefab) is built all the prefabs instances are fully expanded. So rather than a‬
W
‭reference to an external prefab file + property modifications, each prefab instance is replaced by‬
‭all the contents of the prefab, with the property modifications “baked in”. So the scene files are‬
‭self-contained, with prefab structuring removed and not relevant at runtime.‬

I‭t is also possible to put a prefab explicitly into an AssetBundle. In that case any nested prefabs‬
‭are expanded, just as happens in a scene, and the full hierarchy of gameobjects from the prefab‬
‭is included in the AssetBundle. The prefab can be loaded at runtime, which returns the root‬
‭game object of the prefab. Multiple instances can be created by instantiating the loaded Prefab‬
‭multiple times.‬

‭ s with other Asset types it is possible to place multiple Prefabs in the same AssetBundle.‬
A
‭However if the prefabs are very large there can be some overhead if you typically only load a‬
‭small portion of the prefabs in a large AssetBundle. So grouping prefabs that are typically‬
‭loaded together into a single AssetBundle can be better than grouping unrelated prefabs‬
‭together.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭7‬
‭How do Scenes share assets in AssetBundles?‬
I‭n the editor, a scene can reference Assets in the project, for example Materials, Audio and‬
‭ScriptableObjects.‬

I‭n a player build, all assets that are referenced by scenes are built into sharedAsset files, and‬
‭data referenced from multiple scenes is automatically deduplicated so it is only stored once.‬
‭This works because each scene will typically have its own sharedAsset file, but it can also‬
‭reference the sharedAsset files of other scenes.‬

‭ or the purpose of illustrating this concept, imagine Scene1 and Scene2 both have‬
F
‭MonoBehaviours that reference the same Scriptable Object, “[Link]”.‬

‭ he resulting player build will share the [Link], because both level files point to the same‬
T
‭object. Specifically, the [Link] becomes a MonoBehaviour object inside‬
‭[Link] file.‬

‭Now getting to AssetBundles!‬

I‭f you build multiple scenes into a single AssetBundle, then the same build logic is applied to‬
‭create sharedAsset files inside the bundle. So referenced data is only stored once, and multiple‬
‭scenes can reference the same data.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭8‬
‭ o in our example the resulting AssetBundle when both Scene1 and Scene2 is very similar to a‬
S
‭player build.‬

I‭f, instead, you build scenes into separate bundles, then there can be no sharing of implicitly‬
‭referenced content. This is by design because it would be unexpected for scene bundles to‬
‭depend on each other. However it also means that data can be duplicated, and storing scenes‬
‭in separate bundles can result in a larger build output than the equivalent scenes built in the‬
‭Player or as a single AssetBundle. It can also mean that objects like ScriptableObjects that‬
‭might be intended as singletons could have multiple instances.‬

‭ o in the case of our example, the MonoBehaviour object from [Link] will be duplicated in‬
S
‭both bundles, which also means that data won’t be shared at runtime.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭9‬
‭ he general approach to avoid duplication of referenced assets in AssetBundles is to explicitly‬
T
‭assign those assets to an AssetBundle. This approach applies to Scene bundles as well, but‬
‭assets and scenes cannot be located in the same bundle. Therefore, when sharing data‬
‭between scene bundles, you would define one or more additional AssetBundles, and assign the‬
‭shared assets to those AssetBundles. Then the build will automatically share those assets‬
‭rather than duplicating them inside each scene bundle.‬

‭This is what that would look like for our example:‬

‭ ote: The only way to duplicate the ScriptableObject in this case is avoid assigning it explicitly‬
N
‭to any AssetBundle. If the same Asset is explicitly listed in more than bundle definition then we‬
‭fail the build. Some users have requested the ability to intentionally duplicate an asset, for‬
‭example in this‬‭forum post‬‭.‬

‭How can I log the time taken during an AssetBundle build?‬


‭ o log the time taken by a build you could use standard C# timer code, for example‬
T
‭[Link]().‬

‭For example:‬
‭sing UnityEditor;‬
u
using System;‬

using [Link];‬

using UnityEngine;‬

public class NewBehaviourScript‬

{‬

[MenuItem("Assets/Build AssetBundles")]‬

static void BuildAllAssetBundles()‬

{‬

string assetBundleDirectory = "Assets/AssetBundles";‬

if (![Link](assetBundleDirectory))‬

{‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭10‬
[Link](assetBundleDirectory);‬

}‬

‭ar watch = new [Link]();‬


v
[Link]();‬

[Link](assetBundleDirectory,‬

[Link],‬

[Link]);‬

‭[Link]();‬
w
var t = [Link]([Link]);‬

[Link]($"Build time: {[Link]()}");‬

}‬

}‬

‭ or a breakdown of time taken by individual steps that comprise the build, you can look at the‬
F
‭Build Report Inspector‬‭tool.‬

‭Do AssetBundles have a file extension?‬


‭ o, there is no established or expected file extension for asset bundles. An extension can be‬
N
‭provided when defining the assetbundle name, which will be respected. But the AssetBundle‬
‭Variant feature expects that variants are distinguishable by having the same root filename and‬
‭different extensions,so using a file extension is not really compatible with the full AssetBundle‬
‭feature set.‬

‭In the past “.unity3d” was often used as an extension for AssetBundles.‬

‭How does Unity treat uppercase letters in an AssetBundle name?‬


‭ nity automatically lowercases the assetbundle name. For example if the asset bundle name is‬
U
‭“Foo” then the actual file written will be “foo”. This can cause confusion, especially when‬
‭working on a case-sensitive file system like linux.‬

‭ his behavior is not ideal, but is long established, so it could be disruptive to change it to‬
T
‭respecting the input file name..‬

‭ o avoid potentially hitting issues with these different behaviors it is recommended to use‬
T
‭lowercase names for your AssetBundle names.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭11‬
‭ hy are my files reimporting or builds different between Windows‬
W
‭and Mac?‬
‭ heck to make sure the source control system is not converting line endings when checking out‬
C
‭or checking in files. If line endings for files are changed between the windows and posix‬
‭conventions then the hashing of text file contents would change. This can result in imports‬
‭having to run again, builds being dirty or having different binary output and other side effects.‬

‭Are AssetBundle Builds Deterministic?‬


‭ he design of the build is focused on deterministic results, so that repeating the builds more‬
T
‭than once would give the same binary results. This means that different machines could‬
‭produce the same results and it doesn’t matter where an AssetBundle is built, so long as it uses‬
‭the same version of Unity + same version of all packages + the same project content.‬

‭However sometimes the results are not deterministic. Here are some example causes:‬

‭●‬ ‭Bugs or design issues in Unity Features that result in non-determinism.‬


‭○‬ ‭Example: A difference in a math library, causing different rounding of float‬
‭numbers on between CPUs. Fixed in 2023.2‬
‭[Link]
‭s-when-building-asset-bundles‬
‭○‬ ‭Example:‬‭DXT5 format‬‭compression differences for textures‬‭when run on‬
‭different machines. This shows up is binary differences in .ResS files.‬
‭●‬ ‭3rd Party Software issues that influence the content of a project‬
‭○‬ ‭Example: Source control issues such as line-ending transformations.‬
‭●‬ ‭User or package code, running during Build callbacks, [Link] etc.‬
‭○‬ ‭For example, callback code that downloads dynamic information from the internet‬
‭and incorporates it into the build.‬

‭ arger projects will often use one or more dedicated Build Machines (or Virtual Machines) that‬
L
‭have consistent and predictable hardware configurations for their official builds. This can reduce‬
‭the risk of non-determinism in the output. Custom build scripts can also include hashing‬
‭comparisons, and periodically use that to sanity check that their builds are consistent between‬
‭different build machines.‬

‭Known sources of nondeterminism in Assetbundles:‬


‭-‬ ‭The RenderSettings contained within an assetbundle that contains a scene can be the‬
‭source of nondeterminism due to float imprecision between devices - in particular, the‬
‭m_AmbientProbe and m_IndirectSpecularColor members seem to cause problems. Not‬
‭placing scenes within assetbundles could be a potential workaround for this issue.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭12‬
‭-‬ ‭ extures that utilize the Non-power of 2 advanced setting in their importer will sometimes‬
T
‭have differing hashes between devices. Setting the Non-power of 2 setting to “None”‬
‭appears to make the result deterministic.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭13‬
‭Tips and Tricks‬

‭Should AppendHashToAssetBundleName be used?‬


‭ he [Link] flag means that AssetBundles‬
T
‭will be named with their content hash as part of the filename. That means that rebuilding the‬
‭AssetBundle after a change in content will result in a different filename.‬

I‭n some cases this feature can be useful, for example it can be convenient for web hosting of a‬
‭game when different devices are running different versions at the same time. With this naming‬
‭convention different versions of the same assetbundle can exist side by side in the same folder,‬
‭and each device would download the correct one if they have a version specific manifest or‬
‭catalog file.‬

‭This flag is on by default for Addressables.‬

‭ here are some downsides to this. This means that the built-in AssetBundle caching will not‬
T
‭work to automatically replace older versions of an AssetBundle with newer versions. So you will‬
‭end up with multiple versions of the same AssetBundle sitting in the cache using up disk space.‬
‭The older AssetBundles can be removed eventually based on cache size limits.‬

‭ he flag will also foil file-level patching, which is built in on many console platforms. E.g. a one‬
T
‭byte change to a 100MB file would end up treated as an entirely new 100MB file rather than a‬
‭small patch file. A more sophisticated cross-file patching solution could still work but would‬
‭probably need to be a custom solution specifically for AssetBundles, so really seems hard to‬
‭justify compared to not using the flag.‬

‭ ow can I build AssetBundles with different compression‬


H
‭settings?‬
‭ he [Link] API takes a build option argument, which can be used to‬
T
‭pick the compression method (e.g. uncompressed, chunk or full file).‬

I‭n some more advanced situations it can be desirable to have different compression settings for‬
‭different bundles (e.g. to leave some bundles uncompressed because they do not benefit from‬
‭compression). But because the compression setting covers the whole build this is not‬
‭supported directly using the API.‬

‭ ne workaround is to run the build two or more times, with different output folders and different‬
O
‭compression settings. Then the results can be merged back together, picking and choosing‬
‭which output is desired for each bundle. The AssetBundles built this way should have identical‬
‭CRC and hash values so basically be interchangeable.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭14‬
‭ nother workaround is to use Addressables, which supports different compression settings for‬
A
‭each group.‬

‭ ow can I perform partial builds and still use the inspector to‬
H
‭assign Assets to AssetBundles?‬
‭ he built-in UI at the bottom of the Inspector window is useful for assigning Assets to‬
T
‭AssetBundles. That UI stores the assignments in the AssetDatabase. However the signature of‬
‭[Link] which reads those settings will always build ALL bundles. This‬
‭may not be convenient in cases where you only want to build a single bundle or group of‬
‭bundles. Or if you want to do a mix of UI-assigned and programmatic assignments for your‬
‭bundles.‬

‭ he more flexible way to perform a build is by passing an Array of‬‭AssetBundleBuild‬‭structures.‬


T
‭But that API ignores any assignments that have been made in the inspector, and requires that‬
‭the bundles and their contents are assigned manually.‬

‭ useful way to get the best of both alternatives is to use these APIs to populate the‬
A
‭AssetBundleBuild Array:‬

‭‬ A
● ‭ [Link]‬
‭●‬ ‭[Link]‬

‭ his gives an opportunity for the script to potentially build just a subset of the bundles returned‬
T
‭by GetAllAssetBundleNames, or to extend or modify the list of Assets beyond what has been‬
‭assigned in the UI. Once the array of AssetBundleBuild structures has been tweaked it can be‬
‭provided to [Link].‬

‭ ote: in projects that use AssetBundle Variants it is more involved to build the correct‬
N
‭AssetBundleBuild structures. There are some methods to help, e.g.‬
‭[Link]().‬

‭ ote: the API‬‭[Link]()‬‭is actually a‬


N
‭convenient way to populate the AssetBundleBuild structure based on the AssetDatabase‬
‭content. It may be missing the support for AssetBundle Variants that some users would need,‬
‭but otherwise seems to do the same thing as the calls mentioned above. It’s used by the‬
‭Scriptable Build Pipeline and marked in the documentation as internal, but with a bit of testing‬
‭might be confirmed as something that can be ok for users to call in which case we can relax the‬
‭warnings in the documentation.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭15‬
‭ re there issues with Animation Override Controllers and‬
A
‭AssetBundles?‬
‭ hen putting Animation objects into AssetBundles any duplication of the clips can break Assets‬
W
‭like the AnimationOverrideController. The simplest solution is to make sure all the animation‬
‭Assets are included together in the same AssetBundle.‬

‭ ut it also should be possible to split Animation assets into different Assetbundles, so long as‬
B
‭they are all built at the same time and all the Assets are explicitly assigned to an AssetBundle.‬
‭This is important because like all other Asset times, any implicit reference from multiple‬
‭AssetBundle is resolved by embedding the referenced object directly in each bundle, e.g.‬
‭duplicating them. That can lead to clips being duplicated and hence break the behaviour of the‬
‭AnimationOverrideController.‬

‭ re there issues with Scriptable Render Pipelines and‬


A
‭AssetBundles?‬

‭ or HDRP and URP the compilation of shaders can be the longest step in a build and problems‬
F
‭can pop up either with too many shader variants (impossible build times) or missing shader‬
‭variants (shaders missing when the Shader is used in the runtime).‬

‭Some recommendations:‬

‭●‬ W ‭ hen we build the player, we build with SRP settings (HDRP or URP). This causes‬
‭the stripping of various shader variant‬
‭●‬ ‭Then you can build an AssetBundle with some Material, Shader Graph in it then it‬
‭can use the same stripping process than the player.‬
‭●‬ ‭If you change SRP settings in the project, your AssetBundle will break, unless you‬
‭rebuild them as well as your player.‬
‭●‬ ‭The SRP settings should not be included directly inside an AssetBundle.‬
‭And if you try to move any SRP settings inside an AssetBundle, thing will not‬
‭work either as the player will not contain the variant for it‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭16‬
‭Inside AssetBundles‬

‭What is the format of a Unity Archive file?‬


‭ ssetBundles use the Unity Archive file format, which is similar to a zip file and contains one or‬
A
‭more files inside it. The content can be compressed.‬

‭ his file format is not specific to AssetBundles and is used in a few other contexts such as‬
T
‭“Content Files”.‬

‭More details about Archive Files‬

‭ here are also other possible variations of the Archive format that can be loaded, presumably‬
T
‭maintained for backward compatibility with data that was written in the past. The signature and‬
‭flags in the header are used to determine the expected layout. E.g. Current AssetBundle builds‬
‭use signature “UnityFS”, but “UnityWeb”, “UnityRaw” and “UnityArchive” files are also loadable.‬

‭ he Archive file format is implemented in‬‭Runtime\VirtualFileSystem\ArchiveFileSystem.‬ ‭The‬


T
‭archive can be mounted using the Unity VirtualFileSystem (VFS), e.g. when an AssetBundle is‬
‭loaded. Once mounted the files inside are accessible using the same Unity file system APIs that‬
‭are used to enumerate or read regular files.‬

‭ s mentioned, there is nothing AssetBundle-specific in the file format itself. The‬


A
‭AssetBundle-specific meta-data is actually stored as a Unity Object, with a reserved LFID,‬
‭located inside the first Serialized File inside the archive. The following sections describe the‬
‭expected contents of an AssetBundle archive in more detail.‬

‭What is inside an AssetBundle Archive?‬


‭ s mentioned above, AssetBundles are archive files which contain a directory structure. This‬
A
‭can be mounted and accessed by Unity when an AssetBundle is loaded. The Archive file‬
‭format is generic, and this section talks about the expected contents for an AssetBundle Archive‬
‭file.‬

‭ here is always at least one “SerializedFile”. This is Unity’s binary format, e.g. the same format‬
T
‭used when saving Scenes or Assets in Binary instead of YAML. It is also the same format as‬
‭the “level” files produced in a Player Build.‬

‭ egular (non-scene) AssetBundles contain one Serialized File, with a name like‬
R
‭“CAB-cc6c60ef8808e0fc6663136604321554”.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭17‬
‭ ach Scene AssetBundle contains two Serialized Files per Scene, one with no file extension‬
E
‭and the other with the extension “.sharedAssets”. The first file stores the scene contents, e.g.‬
‭the hierarchy of objects directly in the original scene file. The “sharedAssets” stores any objects‬
‭referenced from the scene (e.g. pulled in from other Assets or the built-in resources).‬

‭ bjects from assets that are referenced by any of the Scenes end up in the “.sharedAssets”‬
O
‭files. To avoid duplication the Scene and [Link] files can reference objects inside‬
‭other .sharedAssets files. But there is never any external reference to an object inside a Scene‬
‭SerializedFile.‬

‭AssetBundles can also contain:‬


‭●‬ ‭.resS‬‭files if the SerializedFile‬
‭●‬ ‭.resource‬‭files with Audio data.‬
‭●‬ ‭Global Illumination data for Scene AssetBundles (in a folder named GI)‬

‭These files are named after the serialized file that holds the associated objects.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭18‬
‭ hat is the naming convention for SerializedFiles inside an‬
W
‭AssetBundle?‬

‭ he naming of directories and files inside an AssetBundle is rather technical. These paths are‬
T
‭visible when looking inside an AssetBundle, so this section gives some details to demystify how‬
‭the names are generated. However for regular usage of AssetBundles it is not necessary to‬
‭understand anything about the internal naming.‬

‭ or a regular AssetBundle the name of the SerializedFile is based on the hash of the‬
F
‭assetbundle name. E.g. If the assetbundle is called “mybundle” then the internal file will be‬
‭“cab-”‬‭+ <Hash of‬‭“mybundle”>)‬‭. The hash uses the‬‭MD4 algorithm, which is not the same as‬
‭the Hash128 object in Unity C# API (which is spooky hash).‬

‭ ip: There is C# code in the Scriptable Build Pipeline and BuildPipelineModuleTests that‬
T
‭implements the precise hashing that the native build implementation uses. But for normal user‬
‭scenarios this should not be needed.‬

‭ ote: There is an option to include the AssetBundle hash in the bundle’s filename. That hash is‬
N
‭not included in the string when the SerializedFile name is determined.‬

‭For Scene bundles there are three conventions:‬

‭●‬ B ‭ [Link] uses a name based on the Scene’s file name (without‬
‭path and extension). So “Assets/[Link]” becomes “PlayerBuild-Scene1” . This‬
‭can hit conflicts if the same scene filename exists in different project directories.‬
‭●‬ ‭The new Multi-Process Build Pipeline feature (2023.1 and later) uses “cab-” +‬
‭<AssetDatabase GUID of the scene>.‬
‭●‬ ‭Scriptable Build Pipeline uses the hash of the path, e.g. “cab-” + <hash of scene path>)‬

‭Path convention for Unity Archive Mounting‬

‭ hen an Archive is loaded its contents are mounted into the Unity virtual file system (VFS). To‬
W
‭keep the content of each AssetBundle segregated each bundle has a unique directory name.‬

‭The convention is for the directory to match the name of the first file.‬

‭So, putting it together, the full virtual path to a Serialized file inside a bundle ends up like this:‬

‭Asset Bundle: “archive:/cab-<HashAssetBundleName>/cab-<HashAssetBundleName>”‬

‭Example:‬
“archive:/cab-9fc0d4010bbf28b4594072e72b8655ab/cab-9fc0d4010bbf28b4594072e72b8655ab”‬

‭Scene Bundle:‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭19‬
‭ ‬ “‭ archive:/PlayerBuild-<NameFirstScenePath>/PlayerBuild-<NameCurrentScenePath>”or‬

‭●‬ ‭“archive:/cab-<HashOfFirstScenePath>/cab-<HashOfCurrentScene>”‬
‭●‬ ‭“archive:/cab-<AssetGuidOfFirstScenePath>/cab-<AssetGuidOfCurrentScene>”‬

‭What is inside an AssetBundle SerializedFile?‬


‭Asset Bundle SerializedFiles, like any other Unity binary file, contains the following:‬
‭1.‬ ‭A header section.‬
‭○‬ ‭This includes a list of External References, e.g. listing other files that the objects‬
‭inside the SerializedFile rely on.‬
‭2.‬ ‭A flat list of objects, in binary serialized form.‬

I‭n the case of a regular (non-scene) AssetBundle then the objects are all the objects from the‬
‭explicitly specified assets, plus all the objects that are implicitly included via dependencies. For‬
‭example, when putting multiple ScriptableObject assets together in an AssetBundle then there‬
‭will be one object per ScriptableObject asset file, plus additional objects for the MonoScripts‬
‭referenced by those assets.‬

‭ here are several “special” objects specific to Asset Bundles that are stored along with the other‬
T
‭objects in the SerializedFiles. See the Advanced section for details about the‬‭AssetBundle‬
‭object and the‬‭PreloadData‬‭object.‬

‭What is a .ResS file?‬


‭ he raw pixel data for textures and vertex data for meshes can end up in a resS file inside an‬
T
‭AssetBundle, rather than being stored inline with the objects stored in a SerializedFile. This‬
‭makes it more efficient to upload data to the GPU. This also helps avoid the 2GB size limit on‬
‭Unity Objects.‬

‭ ote: Mesh vertex data will remain inside the SerializedFile if CPU access is required, e.g.‬
N
‭read/write enabled on the importer.‬

‭ hen generated, this file will have the same name as the associated SerializedFile, with the‬
W
‭.ResS extension added on. For example: “CAB-296c403ed80cf6a3f663cb1ae70bdd62‬‭.resS‬‭”‬
‭(inside an Asset Bundle).‬

‭ ote: This type of file can also be generated by Player Builds. E.g. “[Link]” or‬
N
‭“[Link]”.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭20‬
‭How do References To ResS Files work?‬

‭ hen the build produces a .resS file there will be 1 or more references from Objects inside the‬
W
‭associated serialized file to that file.‬

‭ hese references can be determined by expanding the Serialized File with Binary2Text (or‬
T
‭UnityDataTools) and searching for “.resS”.‬

‭ or example, inside the Binary2Text dump of a serialized file (called‬


F
‭CAB-296c403ed80cf6a3f663cb1ae70bdd62) this reference was be found:‬

I‭D: 1 (ClassID: 43) Mesh‬


‭….‬
‭m_StreamData (StreamingInfo)‬
‭offset 0 (UInt64)‬
‭size 2880 (unsigned int)‬
‭path‬
‭"archive:/CAB-296c403ed80cf6a3f663cb1ae70bdd62/CAB-296c403ed80cf6a3f663cb1ae70b‬
‭[Link]" (string)‬

‭That says that the first 2880 bytes of the .ResS file contain the mesh data for that object.‬

‭ o it would be possible to use the binary2text output to determine exactly what is inside a‬
S
‭.ResS, and what ranges of bytes correspond to each object‬

‭Format of the ResS file‬

I‭n the case above the total size of the .ResS file is 2880 bytes. So there is no header or footer‬
‭structure to the .ResS file.‬
‭TBD: is there any sort of padding/alignment inserted between the contents inside a ResS file?‬

‭What is a .Resource file?‬


‭ imilar to a .ResS file, this file can be generated as a companion for a SerializedFile inside an‬
S
‭AssetBundle. It contains the media from any Audio or Video clips serialized inside the‬
‭SerializedFile.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭21‬
‭How can I see what is inside an AssetBundle?‬

‭Option 1: WebExtract + Binary2Text‬

‭ very install of Unity includes two tools that let you expand the content of an AssetBundle.‬
E
‭They are rather low level tools but can be used from the command line or invoked from scripts.‬
‭The precise location varies depending on platform and version of Unity - if you search for‬
‭“WebExtract*” from the root Unity installation folder you will find the precise location.‬

‭ hey are also available if you build Unity yourself (Source Access is available to Enterprise‬
T
‭customers via‬‭[Link]‬‭) (e.g. for example on‬‭a Windows dev machine they can be found‬
‭in <MySourceRoot>/build/WindowsEditor/x64/Debug/Data/Tools) There is also “Binary2Text”‬
‭and “WebExtract” options exposed on the‬‭jam‬‭build‬‭tool‬

‭WebExtract‬

‭ his tool creates a folder based on the assetbundle name, and populates it with the files that‬
T
‭were embedded inside the AssetBundle, similar to expanding a zip file. The folder with the‬
‭expanded contents is created inside the folder where the AssetBundle is located.‬

‭Typical usage from a command line:‬

‭[Link] <path to assetBundle>‬

‭In Windows Explorer you can also drag your AssetBundle onto the [Link] file.‬

‭Binary2Text‬

‭ his tool creates a text dump of the contents of a serialized file (e.g. a Unity binary-format file).‬
T
‭The output file is similar to Unity’s yaml format but it is not a true yaml file.‬

‭ ypical usage after running WebExtract would be similar to:‬


T
‭[Link] <path_to_asset_bundle>_data/CAB-9ac52a515ca1338e37574c57095ff9d9‬

‭ hat creates a .txt file in the same folder as the serialized file, e.g.‬
T
‭<path_to_asset_bundle>_data/[Link]‬

‭ ote: Binary2Text requires that you are running the exact same version of Unity as was used to‬
N
‭create the SerializedFile. This restriction makes it more limited than UnityDataTools. Also for‬
‭large AssetBundles, especially with large hierarchies or shaders the output can be absolutely‬
‭massive (in the gigabytes) and hard to manipulate.‬

‭Related: this external‬‭Support Article‬ ‭also gives‬‭an example of using these tools.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭22‬
‭Option 2: UnityDataTools‬
‭Download and documentation:‬‭UnityDataTools on github‬

‭ his is a newer alternative to WebExtract and Binary2Text that can potentially be a better basis‬
T
‭for building tools that look inside AssetBundles. The text format it outputs when dumping‬
‭serialized files is not the same as Binary2Text but overall it is very similar.‬

‭ tarting with recent version of Unity the native dll that it relies on is included in the Unity‬
S
‭installation, so UnityDataTools is an “official” tool.‬

‭An example usage (once you have downloaded the git repo and built the tool):‬

‭[Link] dump <fullpath-of-assetbundle>‬

‭ his opens the AssetBundle and produces text dumps of each Serialized file in the current‬
T
‭working folder (without requiring to also expand out all the binary files).‬

‭ his tool also has the “‬‭analyse‬‭” option that populates‬‭an sqlite database. This is extremely‬
T
‭useful for looking at the contents of large builds - it runs much faster than Binary2Text and can‬
‭handle object and Asset counts that the UI tools like Build Report Inspector and AssetBundle‬
‭browser cannot handle. After running the tool you can open the resulting “.db” file in a sqlite‬
‭browser, there are freeware tools like “DB Browser” on all platforms. There are useful “views”‬
‭defined that show the content of the AssetBundles in useful ways.‬

‭Option 3: AssetBundles Browser‬

‭ he AssetBundle Browser package is still mentioned in the‬‭manual‬‭and may be useful in some‬


T
‭circumstances, as a UI tool for viewing the contents of AssetBundles. It has not recently been‬
‭updated and is known not to work that well with very large AssetBundles.‬

‭Option 4: Via BuildReport‬


‭ he options above are about looking directly inside AssetBundles. But it is also worth‬
T
‭mentioning that tools that view the BuildReport file can be useful, because that file records all‬
‭the content of each assetbundle, down to the object level.‬

‭See “‬‭Do AssetBundle builds generate a BuildReport?‬‭”.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭23‬
‭ ow can I see what Unity Version was used to build an‬
H
‭AssetBundle?‬
‭ he Unity Version is recorded in the AssetBundle header and in the Serialized files (unless‬
T
‭[Link]‬‭)‬‭is used.‬

‭ ssetBundles are binary files, we have created some‬‭sample code‬‭showing how to read the‬
A
‭header, including the unity version.‬

‭How can I check the Compression for an AssetBundle‬


‭ ssetBundles are Unity Archive files, so the compression that was set on them can be checked‬
A
‭by loading them using the ArchiveFileInterface.‬

‭This is an example (which is also being added to the reference):‬

‭sing
u [Link];‬
using
‭ [Link];‬
using
‭ UnityEditor;‬
using
‭ UnityEngine;‬

‭ublic static class ArchiveUtilities‬


p
{‬

#if UNITY_EDITOR‬

[MenuItem("Debug/Check Archive Compression")]‬

static public void CheckCompression()‬

{‬

string archivePath = [Link]("Pick AssetBundle or other‬

Unity Archive file", "", "");‬

if ([Link] == 0)‬

return;‬

[Link]($"Bundle {archivePath} has compression type‬



{GetArchiveCompression(archivePath)}");‬

}‬

#endif‬

static public [Link] GetArchiveCompression(string‬



archivePath)‬

{‬

var archiveHandle =‬

[Link]([Link], archivePath, "temp:");‬

[Link]();‬

if ([Link] == [Link])‬

throw new [Link]($"Failed to load {archivePath}");‬

‭ar compression = [Link];‬


v
[Link]().Complete();‬

return compression;‬

}‬

}‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭24‬
‭ he mapping between build options passed to [Link] and the‬
T
‭expected compression is as follows:‬

‭Build Flag (BuildAssetBundleOptions)‬ ‭[Link]‬

‭ one (e.g. default behaviour when neither of‬


N ‭Lzma‬
‭the following flags are specified)‬

‭ChunkBasedCompression‬ ‭Lz4HC‬

‭UncompressedAssetBundle‬ ‭None‬

‭ hat are the different “Manifests” generated by an AssetBundle‬


W
‭Build?‬
‭1.‬ ‭.manifest files generated for each AssetBundle‬
‭○‬ ‭This is a YAML format file. It contains:‬
‭■‬ ‭the CRC of the content (for checksum validation of the content)‬
‭■‬ ‭the input hash and typetree hash for incremental build support. Note: In‬
‭newer versions of Unity the content hash will also be added.‬
‭■‬ ‭“Assets” list with the Assets that are loadable from the AssetBundle, e.g.‬
‭the onces that were explicitly specified when building the bundle. It does‬
‭not list Assets that are included implicitly.‬
‭■‬ ‭Dependencies to other assetbundles (as absolute paths, not project‬
‭relative!!)‬
‭■‬ ‭ClassTypes list, specifying what‬‭Unity types‬‭are used.‬‭In the case of‬
‭MonoBehaviours (class 114) each referenced MonoScript Asset is listed‬
‭so that the Scripting classes used by the AssetBundle are listed.‬
‭○‬ ‭It has the same name as the AssetBundle with “.manfest” extension added.‬
‭■‬ ‭However, if the assetbundle has the hash in its name that hash is not‬
‭included in the .manifest file name.‬
‭○‬ ‭These files are used for incremental builds. Certain APIs read from the .manifest‬
‭files, for example‬‭[Link].‬

‭2.‬ ‭.manifest file with the same name as the build folder‬
‭○‬ ‭This is effectively the “root” manifest file‬
‭○‬ ‭This lists the generated AssetBundles and their dependencies‬
‭○‬ ‭Unlike the AssetBundle .manifest files, this file lists AssetBundles with paths‬
‭relative to the build directory, instead of absolute paths.‬
‭○‬ ‭It has the CRC of the Manifest AssetBundle‬
‭○‬ ‭Can be used to avoid Player builds from stripping code that is needed by‬
‭bundles.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭25‬
‭○‬ T ‭ here is no API to read this file (other than‬
‭[Link], [Link])‬‭It‬
‭can be parsed using a YAML parser.‬
‭○‬ ‭It does not contain the CRC and hash values of the individual AssetBundles, so a‬
‭script would need to follow the list of AssetBundles to access their .manifest files.‬

‭3.‬ “‭ Manifest” AssetBundle, named after the build folder. This is a real AssetBundle file. It‬
‭only contains a single SerializedFile, and that serializes the AssetBundleManifest object‬
‭○‬ ‭This object is exposed by the‬‭AssetBundleManifest‬‭object. That object is‬
‭returned by‬‭[Link]()‬
‭○‬ ‭But it is useful to have the information inside an actual AssetBundle because it‬
‭can be distributed in the same way as other AssetBundles, and loading inside‬
‭Unity using the same APIs as other AssetBUndles. For example, typically a‬
‭project might download this file first with‬‭UnityWebRequestAssetBundle‬‭and then‬
‭runtime code can load the AssetBundleManifest object and determine the other‬
‭asset bundles and their hash values so they can also be downloaded. Note:‬
‭when using Addressables the catalog serves a similar purpose.‬
‭○‬ ‭This file is more difficult to read with external tools than the .manifest files.‬

‭// Here is example code for reading the AssetBundleManifest object from the Manifest AssetBundle.‬
[Link] ab =‬

[Link]("mybuild/mybuild");‬

AssetBundleManifest manifest =‬

[Link]<AssetBundleManifest>("AssetBundleManifest");‬

[Link](false);‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭26‬
‭Example Manifest Contents‬

‭ hese are examples of the 3 types of manifest files, based on a simple build that has two‬
T
‭AssetBundles. bundle_prefab contains a MonoBehaviour that references bundle_sobject, which‬
‭contains a single serialized ScriptableObject.‬

‭1)‬ ‭Manifest for the AssetBundle‬

‭ his is an example .manifest file for bundle_prefab AssetBundle. It depends on bundle_sobject‬


T
‭and has Class 114 MonoBehaviour references for both the MonoBehaviour it contains and the‬
‭ScriptableObject type used as a field on the MonoBehaviour.‬

‭anifestFileVersion: 0‬
M
CRC: 159838143‬

Hashes:‬

AssetFileHash:‬

serializedVersion: 2‬

Hash: d6a8bfb48aa045a5e6da1958119abd44‬

TypeTreeHash:‬

serializedVersion: 2‬

Hash: 77b6acfcdc0d79b7c5de2be5fee85e4d‬

HashAppended: 0‬

ClassTypes:‬

- Class: 1‬

Script: {instanceID: 0}‬

- Class: 4‬

Script: {instanceID: 0}‬

- Class: 114‬

Script: {fileID: 11500000, guid: 0652a8087db49d248a409593b2b5624b,‬

type: 3}‬

- Class: 114‬

Script: {fileID: 11500000, guid: 6de340f165a154543a19c1972f8b57fa,‬

type: 3}‬

- Class: 115‬

Script: {instanceID: 0}‬

SerializeReferenceClassIdentifiers: []‬

Assets:‬

- Assets/[Link]‬

Dependencies:‬

-‬

C:/unityprojects/BundleIncrementalBuild2021/Assets/StreamingAssets/bu‬

ndle_sobject‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭27‬
‭ he bundle_sobject.manifest file contains this. It has no dependencies and just references the‬
T
‭ScriptableObject.‬

‭anifestFileVersion: 0‬
M
CRC: 4095657361‬

Hashes:‬

AssetFileHash:‬

serializedVersion: 2‬

Hash: 01c155e080f1c19eab7eacdf3723cae7‬

TypeTreeHash:‬

serializedVersion: 2‬

Hash: 67ef303608757ae63abd4d22b7aff22d‬

HashAppended: 0‬

ClassTypes:‬

- Class: 114‬

Script: {fileID: 11500000, guid: 0652a8087db49d248a409593b2b5624b,‬

type: 3}‬

- Class: 115‬

Script: {instanceID: 0}‬

SerializeReferenceClassIdentifiers: []‬

Assets:‬

- Assets/[Link]‬

Dependencies: []‬

‭2)‬ T
‭ his is an example “root” .manifest file, named after the build directory with .manifest‬
‭extension. Lists all the AssetBundles and their dependencies.‬

‭anifestFileVersion: 0‬
M
CRC: 2309754985‬

AssetBundleManifest:‬

AssetBundleInfos:‬

Info_0:‬

Name: bundle_prefab‬

Dependencies:‬

Dependency_0: bundle_sobject‬

Info_1:‬

Name: bundle_sobject‬

Dependencies: {}‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭28‬
‭3)‬ T
‭ his is a text dump of the AssetBundleManifest object that is inside the Manifest‬
‭AssetBundle. This encodes the list of AssetBundles, their dependencies and their‬
‭hashes.‬

ID: 2 (ClassID: 290) AssetBundleManifest‬



m_Name (string) AssetBundleManifest‬

AssetBundleNames (map)‬

Array<pair>[2]‬

data[0] (pair)‬

first (int) 4‬

second (string) bundle_prefab‬

data[1] (pair)‬

first (int) 5‬

second (string) bundle_sobject‬

AssetBundlesWithVariant (set)‬

Array<int>[0]‬

AssetBundleInfos (map)‬

Array<pair>[2]‬

data[0] (pair)‬

first (int) 4‬

second (AssetBundleInfo)‬

AssetBundleHash (Hash128)‬

bytes[0] (UInt8) 214‬

….‬

bytes[15] (UInt8) 68‬

AssetBundleDependencies (set)‬

Array<int>[1]‬

5‬

data[1] (pair)‬

first (int) 5‬

second (AssetBundleInfo)‬

AssetBundleHash (Hash128)‬

bytes[0] (UInt8) 1‬

….‬

bytes[15] (UInt8) 231‬

AssetBundleDependencies (set)‬

Array<int>[0]‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭29‬
‭Do AssetBundle builds generate a BuildReport?‬
‭ es. When doing a player build this object is returned. For built-in AssetBundles the build‬
Y
‭report is generated and written to “‬‭Library/[Link]‬‭”.‬ ‭That is a Unity Serialized‬
‭file in binary format.‬

‭ ou can view the Build Report in YAML if you copy the binary file somewhere into the project’s‬
Y
‭Assets folder. Or you can have it generated directly as text if you enable the diagnostic flag‬
‭(‬‭Preferences->Diagnostics->BuildReportingEditor->SerializeBuildReportAsText‬‭).‬

‭ he‬‭Build Report Inspector‬‭can be used to visually‬‭view the contents in the Inspector. Also the‬
T
‭Project Auditor‬‭package. Warning: both tools are‬‭currently unable to display results for builds‬
‭that involve many Objects because they try to populate list views with thousands or millions of‬
‭entries. For such large builds you may have to write your own tools to use the Unity‬
‭BuildReport object, or parse through the YAML format of the file directly.‬

‭ ote: some information in the build report is more oriented to Player builds and may be a bit‬
N
‭confusing for Asset Bundles, especially when scene files are included in the build. Update:‬
‭Most of these bugs have been fixed and backported.‬

‭The BuildReport reference has been updated in 2023.1 with an example.‬

‭Note: Scriptable Build Pipeline builds do not generate a build report.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭30‬
‭ here can I find a breakdown of the size of data inside an‬
W
‭AssetBundle‬
‭ he BuildReport contains PackedAsset objects which provide a complete listing of objects with‬
T
‭their sizes. This is rather low level information but could be used by tools to generate a more‬
‭human readable view of the data.‬

‭ or example, after each build the BuildReport is scanned and a large log message is generated‬
F
‭and written to the [Link]. It breaks down the total size by content type and gives a view of‬
‭what media files and other resources are included in the bundle. (Note: Scenes are referred to‬
‭as “Levels” in this output)‬

‭Here is an example:‬

‭undle Name: [Link]‬


B
Compressed Size:359.5 kb‬

Uncompressed usage by category (Percentages based on user generated assets only):‬

Textures
‭ 214.1 kb‬ ‭9.6%‬
Meshes
‭ 8.6 kb‬ 0.4%‬

Animations
‭ 2.8 kb‬ 0.1%‬

Sounds
‭ 89.6 kb‬ 4.0%‬

Shaders
‭ 63.6 kb‬ 2.8%‬

Other Assets
‭ 10.1 kb‬ 0.4%‬

Levels
‭ 1.5 mb‬ 68.9%‬

Scripts
‭ 0.2 kb‬ 0.0%‬

Included DLLs
‭ 0.0 kb‬ 0.0%‬

File headers
‭ 309.0 kb‬ ‭13.8%‬
Total User Assets
‭ 2.2 mb‬ 100.0%‬

Complete build size
‭ 2.2 mb‬
Used Assets and files from the Resources folder, sorted by uncompressed size:‬

128.4 kb‬
‭ 5.7% Assets/Scenes/Scene2/[Link]‬

65.0 kb‬
‭ 2.9% Resources/unity_builtin_extra‬

42.8 kb‬
‭ 1.9% Assets/Textures/[Link]‬

21.5 kb‬
‭ 1.0% Assets/Scenes/Scene2/Lightmap-0_comp_light.exr‬

21.5 kb‬
‭ 1.0% Assets/Scenes/Scene2/Lightmap-0_comp_dir.png‬

5.0 kb‬
‭ 0.2% Assets/Meshes/[Link]‬

4.5 kb‬
‭ 0.2% Assets/Scenes/Scene2/[Link]‬

2.8 kb‬
‭ 0.1% Assets/Animations/AnimOnNestedPrefab_Child_01.anim‬

0.9 kb‬
‭ 0.0% Assets/Materials/Mat01_var02_var01_var01.mat‬

0.9 kb‬
‭ 0.0% Assets/Materials/Mat01_var02_var01.mat‬

0.9 kb‬
‭ 0.0% Assets/Materials/Mat01_var03.mat‬

0.9 kb‬
‭ 0.0% Assets/Materials/Mat01_var02.mat‬

0.9 kb‬
‭ 0.0% Assets/Materials/Mat01_var01.mat‬

0.5 kb‬
‭ 0.0% Assets/Prefabs/NestedPrefab_Child.prefab‬

0.5 kb‬
‭ 0.0% Assets/Animations/NestedPrefab_Child.controller‬

0.2 kb‬
‭ 0.0% Assets/AssetBundleData/[Link]‬

0.2 kb‬
‭ 0.0% Assets/Prefabs/[Link]‬

0.1 kb‬
‭ 0.0% Assets/Prefabs/[Link]‬

0.1 kb‬
‭ 0.0% Assets/Scripts/[Link]‬

0.1 kb‬
‭ 0.0% Assets/Scripts/[Link]‬

0.1 kb‬
‭ 0.0% Assets/Scenes/[Link]‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭31‬
‭CRC and Hash Topics‬

‭How do CRCs work with AssetBundles?‬


‭ he CRC is calculated on the entire uncompressed internal contents of the bundle. So it does‬
T
‭not include the AssetBundle header, nor the compression algorithm. It is intended for validating‬
‭that a downloaded assetbundle has been correctly downloaded and recompressed.‬

‭ runcation or other file corruptions that can happen during asset bundle download (just like any‬
T
‭other web download) is one of the common causes of crashes on user devices. This can be‬
‭more prone to happen on older devices which may have less local resources (e.g. running out‬
‭of memory or storage during a download). Such crashes are hard to diagnose because they‬
‭only appear later on during game play and only randomly. Therefore some sort of validation of‬
‭downloaded content, such as the CRC check, is very important to confirm that correct file‬
‭content is actually downloaded. When this check fails then some retry logic will typically fix the‬
‭problem.‬

‭There are some issues with CRC:‬

‭●‬ T ‭ here is a performance cost, especially when the CRC check is being repeated at each‬
‭AssetBundle load. For best performance, doing the CRC check at download time but‬
‭not at load time can be a good compromise. This is also discussed in the “Platform‬
‭Topics” section.‬
‭●‬ ‭As mentioned elsewhere about hashes: by default the Unity Editor version is included in‬
‭the header of SerializedFiles inside the AssetBundle. So doing a rebuild with a new‬
‭version of Unity will change the CRC, even if everything else in the bundle is identical.‬

‭What is the AssetBundle Hash generated by a build?‬


‭ hen building AssetBundles Unity will generate a hash for each AssetBundle. This Hash is‬
W
‭exposed in several ways:‬

‭‬ E
● ‭ xposed by‬‭[Link]‬
‭●‬ ‭Serialized inside the Manifest AssetBundle‬
‭●‬ ‭Recording inside the .manifest file (as‬‭AssetFileHash‬‭)‬

‭ he Native AssetBundle pipeline uses the hash for incremental builds, to determine whether an‬
T
‭AssetBundle needs to be regenerated. The hash is also sometimes used as a version hash, in‬
‭conjunction with‬‭UnityWebRequestAssetBundle‬‭.‬

‭The hashing of AssetBundles is somewhat more complicated than one might initially expect.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭32‬
‭ here are several reasons that we ended up with something more complicated that just hashing‬
T
‭the full file content:‬

‭●‬ T ‭ he compression of AssetBundles can change, so hashing the content of the file itself‬
‭might not give the desired results. For example AssetBundles are typically transferred‬
‭with LZMA compression, which is optimal for file size, and then dynamically‬
‭recompressed by the download cache code into LZ4 Runtime format, which is optimized‬
‭for fast decompression.‬
‭●‬ ‭By default AssetBundles record the exact Unity Editor version in both the AssetBundle‬
‭header and in the header of the SerializedFile(s) inside the bundle. That means that the‬
‭AssetBundle file (and CRC) will change after upgrading Unity, even if nothing has‬
‭changed in the actual built output data. The‬
‭[Link]‬‭flag can be passed to‬
‭[Link] to avoid this issue.‬

‭The different implementations of AssetBundle building calculate the hash in different ways:‬

‭ . Native AssetBundle Hash Calculation‬


1
‭The native AssetBundle build code calculates the hash based on the inputs to the AssetBundle,‬
‭not the built output. This is implemented in AssetBundleHashesEqual() in‬
‭[Link]. This approach has flaws because it does not track every possible‬
‭global setting or input that might influence the build. It also doesn’t include the Unity version.‬
‭Hence is recommended to use the‬‭[Link]‬‭flag‬
‭when building an official release, to be sure that each AssetBundle is full regenerated. The‬
‭potential exists that different assetbundle content will have the same hash, which makes it a‬
‭poor value to use as a definitive unique version for AssetBundles. For example if this hash is‬
‭used as the version value for the AssetBundle cache then if a new bundle is built that happens‬
‭to have the same hash then devices with the old AssetBundle will not download the correct file.‬
‭See the later discussion of UnityWebRequestAssetBundle for more about AssetBundle‬
‭versions.‬

‭ . Scriptable Build Pipeline Hash Calculation‬


2
‭The hash calculation done by the Scriptable Build Pipeline is not the same as that performed by‬
‭[Link]. The SBP algorithm hashes the uncompressed content,‬
‭without the AssetBundle header, and without the SerializedFile header. So it is similar to the‬
‭CRC calculation, but tries to avoid the UnityEditor version problem.‬

‭ n unfortunate side effect of skipping the SerializedFile header can occur if a change occurs in‬
A
‭the headers (e.g. a padding change). If that happens the content hash is not changed even‬
‭though the rebuilt AssetBundle file is different.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭33‬
‭ he SerializedFile header also includes the external references. So moving an external object‬
T
‭from one bundle to another might not impact the content hash. To fix this SBP‬‭will also add in‬
‭the dependent AssetBundles into the hash calculation‬‭.‬‭Unfortunately the dependencies‬
‭used are recursive, rather than the expected “direct dependency” list.‬

‭ his hashing approach also does not include the bundle directory info in the hash, so two‬
T
‭AssetBundles containing different files that had identical contents could produce the same hash.‬
‭In practice that is very unlikely to be an issue, because Unity controls the internal file names‬
‭inside an AssetBundle archive based on the AssetBundle name or name of Scene files.‬

‭3. Multi-Process Asset Bundle Building Hash Calculations‬

I‭n the Multi-Process Asset Bundle Building implementation (available starting in 2023.1), the‬
‭hash calculation is simply the Hash of all the uncompressed file content, using the “spooky‬
‭hash” algorithm. It matches closely how the CRC is calculated. It is recommended to use‬
‭AssetBundleStripUnityVersion‬‭to avoid the hash changing‬‭when the version changes. It does‬
‭not include the names of the file inside the archive as part of the hash because the directory‬
‭information is stored separately from the data inside a Unity Archive file.‬

‭ ore about hashing‬


M
‭Because of all these complexities it is not generally possible for customers to write their own‬
‭hashing calculation code that examines an AssetBundle and produces the same Hash as Unity‬
‭does.‬

‭ he‬‭[Link] demonstrates a way to‬


T
‭calculate hashes for the content, using WebExtract. It was created prior to the availability of‬
‭[Link]‬‭so it uses Binary2Text to avoid the‬
‭SerializedFile header, which is a valid approach but might slow down the calculation for large‬
‭AssetBundles. The result it produces is not equivalent to the internal Unity hash calculation.‬

‭ ashes for AssetBundle also appear as a way of tracking file versions in the API for‬
H
‭downloading AssetBundles. This is covered in the next section.‬

‭ ote: there is also a build flag‬‭[Link]‬‭that‬


N
‭can be used to include the hash directly as part of the output assetbundle file name.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭34‬
‭How do Incremental Builds use the AssetBundle Hash?‬
‭Note: some of this information has been published in a‬‭forum post‬‭.‬

I‭n the Native AssetBundle implementation the AssetBundle hash is used to capture the contents‬
‭and dependencies of the AssetBundle.‬

‭The flow is as follows:‬


‭●‬ ‭Calculate the AssetBundle hash based on the current inputs (see later section for‬
‭details)‬
‭●‬ ‭If there is a .manifest file from a previous build of the AssetBundle then load the hash‬
‭and compare‬
‭●‬ ‭If the hashes match then also calculate the TypeTreeHash‬
‭●‬ ‭If the AssetBundle Hash and TypeTreeHash both match then do not rebuild the‬
‭AssetBundle (unless the ForceRebuildAssetBundle flag is specified)‬
‭●‬ ‭If the AssetBundle is built then the AssetBundle and TypeTree hash are serialized in the‬
‭newly generated .manifest file.‬

‭This is an example Manifest, showing the data that supports the incremental build.‬

‭anifestFileVersion: 0‬
M
CRC: 2088487739‬

Hashes:‬

AssetFileHash‬
‭ :‬

serializedVersion: 2‬

Hash:‬‭
‭ 01c155e080f1c19eab7eacdf3723cae7‬
TypeTreeHash‬
‭ :‬

serializedVersion: 2‬

Hash:‬‭
‭ 55501093163a37cf23c863ea4050548f‬
HashAppended: 0‬

ClassTypes:‬

- Class: 114‬

Script: {fileID: 11500000, guid: 0652a8087db49d248a409593b2b5624b, type: 3}‬

- Class: 115‬

Script: {instanceID: 0}‬

SerializeReferenceClassIdentifiers: []‬

Assets:‬

- Assets/[Link]‬

Dependencies: []‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭35‬
‭How is the AssetBundle Input Hash calculated?‬
‭ he Native AssetBundle AssetBundle input hash is calculated by hashing a series of inputs that‬
T
‭include:‬
‭●‬ ‭TargetPlatform, subtarget‬
‭●‬ ‭Explicitly and implicitly included assets (The [Link] from the AssetDatabase)‬
‭●‬ ‭Names of the AssetBundles that it depends on‬
‭●‬ ‭Mesh stripping setting‬
‭●‬ ‭Certain‬‭BuildAssetBundleOptions‬
‭●‬ ‭For scene bundles: certain global lighting settings (e.g. lightmap mode, fog mode, etc)‬
‭●‬ ‭Shader platform / graphics APIs‬
‭●‬ ‭For bundles with shaders: certain Render Pipeline assets‬

‭ lthough a pretty exhaustive calculation, this does not capture every possible influence that can‬
A
‭impact the build. Some known limitations include:‬
‭●‬ ‭If a script class keeps the same class name, but moves to a new assembly or‬
‭namespace then the hash does not change‬
‭●‬ ‭If an asset inside a dependent AssetBundle moves to another AssetBundle the hash‬
‭does not change.‬

‭ hese cases of incomplete hashing can have serious impact with the potential of null‬
T
‭references, crashes or other unexpected failures on end user devices. If a rebuild is forced with‬
‭[Link]‬‭flag,‬‭then the correct new content would be‬
‭generated, but any code depending on the hash as a “version” for the AssetBundle would not‬
‭recognize that the AssetBundle has changes.‬

‭What is the TypeTreeHash?‬

‭ he Native AssetBundle build implementation uses a second hash value during Incremental‬
T
‭build calculations called the‬‭TypeTreeHash.‬

‭This hash is also recorded in the .manifest file for the AssetBundle.‬

‭ he hash is derived from all the types involved in the AssetBundle. These types are listed in the‬
T
‭ClassTypes section of the manifest file. For each type the hash of the typetree is feed into the‬
‭TypeTreeHash.‬

‭For Script types the ClassName, Namespace and Assembly are also hashed.‬

‭ he purpose of this hash is to detect whether any objects used in the AssetBundle have newer‬
T
‭serialization formats. For example adding new fields to a MonoScript or updating to a new‬
‭version of Unity that changes some built-in objects. A change in serialization format means that‬
‭the AssetBundle should be regenerated to reflect the latest serialized schema for those objects.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭36‬
‭ nity does provide its best effort to have backward compatibility to older serialized schemas,‬
U
‭e.g. when TypeTrees are included in the AssetBundle, but it is normally best to regenerate if any‬
‭type changes.‬

‭ arning: this typetree hash is not part of the AssetBundle hash. So while changes in this hash‬
W
‭can force an incremental build, it doesn’t force a change in the overall hash value for the‬
‭AssetBundle. This is one of the reasons that the AssetBundle hash is not an ideal value for‬
‭tracking file versions.‬

‭ his check can be disabled by specifying the‬


T
‭[Link]‬‭flag.‬

‭For more details see‬‭CalculateClassCompatibilityHash()‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭37‬
‭Downloading AssetBundles‬

‭ hat built-in mechanism does Unity offer for downloading‬


W
‭AssetBundles?‬
‭ ne of the main purposes of AssetBundles is to be able to distribute them independently of the‬
O
‭original player build, e.g. to add or improve the content of a game.‬

‭ ssetBundle files can be distributed anyway that the customer wishes, for example hosting‬
A
‭them somewhere on the internet and using custom http code to download them, or using a‬
‭devices built in DLC mechanism.‬

‭ owever Unity offers a built-in‬‭UnityWebRequestAssetBundle‬‭API that can make it easier to‬


H
‭incorporate AssetBundle downloading into a game, especially because it supports caching and‬
‭runs on all supported platforms.‬

‭ ote: When using Addressables, the same UnityWebRequestAssetBundle API is used under‬
N
‭the hoods, hiding most of the details. But because of the potential memory usage or‬
‭performance costs it is useful to understand how it works and to configure certain settings like‬
‭compression and CRC checking properly.‬

‭ hat recompression happens to AssetBundles when entering the‬


W
‭cache?‬
‭ hen an AssetBundle is distributed it is typically in LZMA format. It is then recompressed on‬
W
‭the client to LZ4 as it is downloaded and put into the cache.‬

‭ his is done because LZMA is optimal for file size, e.g. for fast transfers, but LZ4 is optimized‬
T
‭for fast decompression, e.g. for faster load times.‬

‭ ven uncompressed AssetBundles will be recompressed to LZ4 when downloaded and cached‬
E
‭via UnityWebRequestAssetBundle.‬

‭ he AssetBundles can be cached in uncompressed format by setting‬


T
‭[Link]‬‭to false.‬

‭See AssetBundleLoadFromStreamAsyncOperation::FeedStream for more internal details.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭38‬
‭ hich signatures of‬
W
‭[Link] support caching?‬
‭[Link] offsets many signatures.‬

‭ hese signatures use the cache. In the case a uint version is provided the cache generates the‬
T
‭equivalent Hash128 value for use in the cache.‬

‭nityWebRequest GetAssetBundle(string uri, uint version, uint crc);‬


U
UnityWebRequest GetAssetBundle(Uri uri, uint version, uint crc);‬

UnityWebRequest GetAssetBundle(string uri, Hash128 hash, uint crc);‬

UnityWebRequest GetAssetBundle(Uri uri, Hash128 hash, uint crc);‬

UnityWebRequest GetAssetBundle(string uri, CachedAssetBundle‬

cachedAssetBundle, uint crc);‬

UnityWebRequest GetAssetBundle(Uri uri, CachedAssetBundle‬

cachedAssetBundle, uint crc);‬

‭ hese signatures do not support caching, because they do not accept a Hash or version‬
T
‭argument:‬

‭nityWebRequest
U GetAssetBundle(string uri);‬
UnityWebRequest
‭ GetAssetBundle(Uri uri);‬
UnityWebRequest
‭ GetAssetBundle(string uri, uint crc);‬
UnityWebRequest
‭ GetAssetBundle(Uri uri, uint crc);‬

‭ hat happens when‬


W
‭[Link] is used without‬
‭caching?‬
‭ hen downloading an assetbundle without providing the version, the bundle is downloaded and‬
W
‭assembled in LZ4 format as a temporary memory file, then loaded.‬

‭ s mentioned more in the “Platform Topics” section, some platforms do not have caching‬
A
‭enabled, so there is potentially significant memory usage when AssetBundles are loaded.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭39‬
‭ ow does [Link]‬
H
‭determine the bundle name for the cache?‬
‭ y default, the last component of the download URL is assumed to be the bundle name.‬
B
‭However in cases where this is not appropriate then the assetbundle name can be specified in‬
‭the [Link] structure that can be passed in some signatures of‬
‭[Link].‬

‭ ote: As mentioned in the reference the full URL is not taken into account for the caching, only‬
N
‭the assetbundle name. This is different from general http caching, which would typically work‬
‭based on the full URL.‬

‭What is the Hash used by the AssetBundle cache?‬


‭Hash values are used as the “version” of an AssetBundle in the download cache.‬

‭ hen downloading an AssetBundle with UnityWebRequestAssetBundle it is up to the user to‬


W
‭provide any 128-bit (hash) or 32-bit (uint) value they like to distinguish the “version” of the‬
‭AssetBundle. This could be the hash value calculated by the AssetBundle build, or a regular‬
‭numeric version number (1,2,3…) or some other value that fits into the customer’s build and‬
‭release system. It is important to update the version when a new AssetBundle build is released‬
‭for download, so that devices will download and use the newer version instead of a previous‬
‭versions that could be cached locally.‬

I‭mportant:‬‭Unity does not perform hash calculations‬‭on AssetBundles during the download and‬
‭load action. So providing a version hash argument is NOT a way to validate that the contents‬
‭match the expected hash. Instead the AssetBundle CRC is available for validation purposes.‬
‭The version hash is just an identifier to distinguish different builds of a particular AssetBundle‬
‭filename.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭40‬
‭ ow can I use the AssetBundle hash as a version hash when‬
H
‭downloading an AssetBundles?‬
‭ s mentioned above, the AssetBundle cache requires a version hash. This hash is not‬
A
‭necessarily a value that can be calculated directly from the file itself, rather it is left up to the‬
‭user to track and specify a hash version when downloading AssetBundles. This gives flexibility‬
‭but puts some burden on the developer to understand and create a system.‬

‭ ne widely used hash is the “AssetFileHash” hash value generated by the build (as described in‬
O
‭an earlier section).‬

‭ arning:‬‭This section describes how to use that hash‬‭as a version for the AssetBundle cache,‬
W
‭but it may be more prudent to use an alternative method (as described in more detail in the next‬
‭section). However the hash generated by the native AssetBundle API has flaws and sometimes‬
‭a new build of an AssetBundles could have the same hash as an older build with slightly‬
‭different content. See‬‭“What is the AssetBundle Hash‬‭generated by a build?”‬‭for more‬
‭information. The Multi-Process option introduced in 2023.1 does not have this problem, nor‬
‭does the Scriptable Build Pipeline, so it is safer to use hashes from those pipelines as a version‬
‭hash.‬

‭ hen doing an AssetBundle build there are various output files, including .manifest files and the‬
W
‭Manifest AssetBundle. The hash values for each individual asset bundle is recorded in its‬
‭.manifest file.‬

‭For example:‬
‭anifestFileVersion: 0‬
M
CRC: 2210572199‬

Compression: None‬

Hashes:‬

AssetFileHash:‬

serializedVersion: 2‬

Hash:‬‭
‭ ef053dac9adb3c2f517c180a27afc865‬

‭And the list of all hashes is found in the Manifest AssetBundle.‬

‭ nityWebRequestAssetBundle is only used to distribute AssetBundles, not the .manifest files,‬


U
‭so those .manifest files are not available at download time unless distributed another way.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭41‬
‭ he “manifest AssetBundle” is a special AssetBundle, so it can be downloaded by‬
T
‭UnityWebRequestAssetBundle. A typical usage might be like this:‬
‭●‬ ‭For each new build upload the new manifest AssetBundle along with any changed or‬
‭new AssetBundles.‬
‭●‬ ‭When checking for updates the player code will download the manifest assetbundle,‬
‭possibly without using any caching because it is a small file.‬
‭●‬ ‭The manifest assetbundle is loaded and‬‭GetAllAssetBundles‬‭and‬
‭GetAssetBundleHash(bundleName)‬‭called to determine‬‭the current list of AssetBundles.‬
‭●‬ ‭UnityWebRequestAssetBundle is called on all the AssetBundles, specifying the reported‬
‭hash. If the file is already in the cache with that same hash then it is not downloaded‬
‭again. If the hash has changed, or the file is new, then it is downloaded.‬

‭ ow can I generate my own version hash when downloading an‬


H
‭AssetBundles?‬
‭ s mentioned above, there are some flaws in the native AssetBundle calculation for hashes, so‬
A
‭that is not necessarily a fully robust value to use as a unique version.‬

‭ he UnityWebRequestAssetBundle gives users the ability to specify any value as the hash,‬
T
‭which is only used to check if an existing cached file matches the requested file. The file‬
‭downloaded from the provided URL is not automatically checked in any way to see if it matches‬
‭the specified file. (But the CRC, if provided, is checked).‬

‭Some possible values to use as a version hash:‬

‭●‬ H ‭ ash of the CRC. The CRC itself can serve as a version identifier. Because it is‬
‭calculated based on the uncompressed content it is resilient to compression changes,‬
‭and it is based on the actual built content, so does not have the flaws of the Native‬
‭AssetBundle hash calculation.‬
‭●‬ ‭Content hash of the AssetBundle file (e.g. MD5) This is simple to generate and check.‬
‭But users need to be aware that LZMA AssetBundles are recompressed to LZ4 when‬
‭being added to the cache, which would change the content hash.‬
‭●‬ ‭AssetBundle hash + the CRC. Combining the CRC value with the hash value generated‬
‭by Unity would address the risk of hash collisions possible in native AssetBundle‬
‭heuristic.‬
‭●‬ ‭Hash of the .manifest file.‬
‭●‬ ‭A numeric versioning scheme, e.g. 1, 2, 3. 4…. , with the version updated each time the‬
‭content changes or for each new build (if caching is not important).‬
‭●‬ ‭etc.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭42‬
‭ o matter what method is used, there needs to be a mechanism to distribute these versions as‬
N
‭a new build is uploaded. This could be a simple JSON file that is generated as a post-build‬
‭step, and the player build will download this file as a first step of checking for updated‬
‭AssetBundles.‬

‭What is the structure of the Asset Bundle Cache?‬


‭ hen using‬‭UnityWebRequestAssetBundle‬‭, a local AssetBundle‬‭cache is used to avoid‬
W
‭downloading the same AssetBundle more than once..‬

‭The cache exists at a root cache folder, with the following structure:‬

RootCacheFolder‬

/ BundleName1‬

/Hash1‬

__data‬

__info‬

….‬

/BundleName2‬

…‬

‭‬ T
● ‭ he hash is used to uniquely identify the assetbundle version.‬
‭●‬ ‭The __data file is the assetbundle content.‬
‭●‬ ‭The __info file records cache meta-data, e.g. last time the file was accessed.‬

‭ uring a download, a temporary directory is used for the data as downloaded, then the‬
D
‭recompressed asset is established into the structure described above.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭43‬
‭Loading Local AssetBundles‬

‭ hat is the performance problem with LZMA compression and‬


W
‭local AssetBundles?‬
‭ y default AssetBundles are built with LZMA compression, which produces the smallest file‬
B
‭sizes. If you download such files from the web and use Unity’s built in caching support then the‬
‭conversion to chunk-based compression (LZ4) only happens the first time it is downloaded, as‬
‭the cached file is written.‬

‭ ut if you use‬‭[Link]()‬‭, then an‬‭LZMA file will be fully converted to LZ4‬


B
‭and the result stored in a temporary memory-based file, then the AssetBundle is loaded from‬
‭that location. This can be inefficient in terms of memory usage and loading time. See also the‬
‭later section “When does Unity load the entire AssetBundle into memory?” for additional details.‬

‭ herefore unless UnityWebRequestAssetBundle and the caching support is used, it can be‬
T
‭better to build with LZ4 or uncompressed formats, so that the file can be loaded incrementally‬
‭and efficiently with [Link]. Alternatively the file can be built and distributed‬
‭in with LZMA format and then converted on the client device using‬
‭[Link] to mimic the behavior of the‬
‭UnityWebRequestAssetBundle.‬

‭ ote: UnityWebRequestAssetBundle can be used to load local files (e.g. using an URL of the‬
N
‭form “[Link] If caching is used then the file will be automatically converted to LZ4‬
‭(if conversion is required) and saved in the cache. Then later requests will load the cached‬
‭version instead of the original file. This can be convenient for writing code that deals with local‬
‭files exactly the same as remotely hosted files, but the downside is that it ends up with two‬
‭copies of the file.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭44‬
‭ hat does the Assert “PersistentManager: File …. Was expected‬
W
‭to exist…” Mean?‬

‭When running developer player builds you may see Asserts logged like:‬

‭ersistentManager: File‬
P
'archive:/CAB-f9439276f830d384c35f627354aecbef/CAB-f9439276f830d384c35‬

f627354aecbef' was expected to exist at absolute path‬

'archive:/CAB-f9439276f830d384c35f627354aecbef/CAB-f9439276f830d384c35‬

f627354aecbef'‬

‭ his very technical message can be a sign that an attempt is being made to load an Asset is‬
T
‭being loaded which depends on an object inside another AssetBundle that wasn’t loaded. E.g.‬
‭it can be fixed by making sure the dependent AssetBundles have been loaded.‬

‭Can UnityWebRequestAssetBundle be used with local files?‬


‭ es, if the URL is correctly formatted with the file protocol and the path to a local file, e.g. “[Link]
Y
‭then an AssetBundle can be loaded via the webrequest code path.‬

‭ his is useful for testing as caching, compression conversion and other internal features can be‬
T
‭triggered, without the need for an actual webserver.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭45‬
‭Performance‬

‭What memory is used when loading Asset Bundles?‬


‭ or LZ4-base AssetBundles:‬‭when an LZ4-based AssetBundle‬‭is open the chunks are‬
F
‭decompressed on demand by the ArchiveStorageReader. There is a cache‬
‭(ArchiveStorageReader.m_CachedBlocks) which helps avoid multiple small sequential reads‬
‭from continually decompressing the same larger chunk. In practice only a block is read and‬
‭cached at a time (‬‭ArchiveStorageReader::kAllowedCacheBlocksCount=1),‬‭so the total RAM‬
‭used for decompressed data per-AssetBundle is small. Its possible that when loading many‬
‭AssetBundles at the same time then the memory used by each block cache will add up to‬
‭something more significant.‬

‭ here is also a temporary cache in ArchiveStorageReader::BatchingFileReader of up to 4MB.‬


T
‭This allocation is made each time a large (multi-block) read call is made and then freed so it‬
‭won’t linger in memory but may show up when profiling memory allocation/usage.‬

I‭n all cases during read:‬‭The Persistent Manager loads‬‭each object by reading from a‬
‭SerializedFile inside the AssetBundle. There is a special cache with pages of data from‬
‭SerializedFiles. It is shared between all loaded AssetBundles, called the‬
‭PooledFileCacherManager.‬ ‭Its default size is‬‭1MB‬‭(16 pages of 64KB), but an internal C# API‬
‭can be used to adjust it. This layer sits above the mounted Archive and uses an abstract‬
‭interface‬‭CacheReaderBase,‬‭so it is not involved with‬‭the underlying file range mapping and‬
‭decompression that happens in‬‭ArchiveStorageReader.‬

‭ here are some additional supporting data structures for mounted Unity Archives and‬
T
‭AssetBundles that will use some modest memory. And the Assets and Scenes loaded from an‬
‭AssetBundle will use memory that is owned by the instantiated Unity objects.‬

‭ verall the RAM usage for an AssetBundle can be small. But in some cases the entire‬
O
‭AssetBundle also gets copied into RAM, that is more of a concern and is covered in the next‬
‭section.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭46‬
‭When does Unity load the entire AssetBundle into memory?‬
‭ his table summarizes the use of in-memory files and format conversions when loading‬
T
‭AssetBundles of different formats using the available loading APIs.‬

‭Asset Bundle Compression‬

‭LZMA‬ ‭LZ4‬ ‭Uncompressed‬

‭[Link]‬ ‭Convert and Open‬


A ‭ ead content directly from‬
R ‭ ead content directly from‬
R
e(),‬
‭ ‭in-memory file‬ ‭file‬ ‭file.‬
LoadFromFileAsync()‬

‭[Link]‬ ‭Convert and Open‬


A ‭ opy and Open‬
C ‭ onvert and Open‬
C
ory()/‬
‭ ‭in-memory file‬ ‭in-memory file‬ ‭in-memory file‬
LoadFromMemoryAsync()‬

‭[Link]‬
C
d = true‬

‭[Link]‬ ‭Convert and Open‬


A ‭ ead content directly from‬
R ‭ ead content directly from‬
R
eam()/‬
‭ ‭in-memory file‬ ‭filestream‬ ‭filestream‬
LoadFromStreamAsync()‬

‭nityWebRequestAssetBun‬ D
U ‭ ownload AssetBundle‬ ‭ ownload and stream to‬
D ‭ ownload and stream to‬
D
dle.‬
‭ ‭and stream to cache file,‬ ‭the cache file, no‬ ‭cache file, converting to‬
GetAssetBundle()‬
‭ ‭converting LZMA to LZ4‬ ‭compression conversion‬ ‭LZ4‬
‭on the fly‬ ‭required‬
‭With Empty Cache:‬ ‭ oad the file from the‬
L
‭●‬ ‭Version Argument‬ ‭Load the file from the‬ ‭ oad the file from the‬
L ‭cache‬
‭provided‬ ‭cache‬ ‭cache‬
‭●‬ ‭File not cached yet,‬
‭●‬ ‭[Link]‬
‭abled = true (this is‬
‭default)‬

‭nityWebRequestAssetBun‬ R
U ‭ ead content directly from‬ ‭ ead content directly from‬
R ‭ ead content directly from‬
R
dle.‬
‭ ‭cached LZ4 file‬ ‭cached LZ4 file‬ ‭cached LZ4 file‬
GetAssetBundle()‬

‭With Cached AssetBundle:‬


‭●‬ ‭Version Argument‬
‭provided‬
‭●‬ ‭File already cached‬

‭nityWebRequestAssetBun‬ D
U ‭ ownload, Convert and‬ ‭ ownload and stream to‬
D ‭ ownload and stream to‬
D
dle.‬
‭ ‭Open in-memory file‬ ‭memory-based temp file‬ ‭memory-based temp file,‬
GetAssetBundle()‬
‭ ‭(no conversion)‬ ‭(no conversion)‬

‭ o Caching (No version arg, or‬


N
‭on platform without‬
‭ENABLE_CACHING)‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭47‬
‭Note: “Convert and Open in-memory file” includes the following steps‬

‭‬ O
● ‭ pen source to check format, close again‬
‭●‬ ‭Convert to LZ4 as in-memory archive file (or uncompressed when‬
‭[Link] is false)‬
‭●‬ ‭Open the in-memory file‬
‭●‬ ‭When unloaded and no more readers: Delete in-memory file‬

‭ ow can I reduce runtime memory usage in AssetBundles with‬


H
‭many Assets?‬
‭ hen loading assets the recommended way to load the asset is by its project relative path (or‬
W
‭the Addressables name if one was specified at build time)‬

‭ nity also supports loading the asset by just its filename, or filename without extension. But‬
U
‭that is implemented by building extra string tables to support those lookups. In AssetBundles‬
‭with many loadable Assets these extra string tables can start to use some noticeable memory.‬

‭ o avoid this it is best practice to always load Assets using the exact key and to turn off the‬
T
‭extra matching capability at build time:‬

‭‬ B
● ‭ [Link]‬
‭●‬ ‭[Link]‬

‭See also the documentation for the‬‭name‬‭parameter‬‭in‬‭[Link]‬‭.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭48‬
‭Platform Topics‬
‭This section covers platform-specific considerations for AssetBundle usage.‬

‭Can I load the same AssetBundles on different platforms?‬


I‭n the editor you can load any AssetBundle, regardless of what platform it was built for. But for‬
‭best results it is best if the Editor’s Player settings have been changed to match that platform.‬

I‭n the runtime you can only load AssetBundles that have been built for the correct target. E.g.‬
‭Android can only load Android AssetBundles. That is because AssetBundles may contain‬
‭platform specific formats for and not work properly, possibly leading to hard to diagnose failures‬
‭such as missing textures. The loading prevention is primarily meant to help detect possible‬
‭problems in a user’s build and release pipeline, e.g. to make sure that content is not accidentally‬
‭being shipped or delivered to the wrong platform.‬

‭ ote: in the case of audio assetbundle and other core formats this may be overly restrictive, but‬
N
‭it is not clear how software could judge if a particular bundle has contents that are safe between‬
‭platforms.‬

‭ here is no check at load time to make sure that the subtarget matches between the player and‬
T
‭the assetbundle.‬

‭How are new AssetBundles downloaded to consoles?‬


‭ ypically AssetBundles are not downloaded directly from the web with code running inside the‬
T
‭game. Instead they are more to be delivered via the platform's own DLC mechanism. These‬
‭DLCs are purchased, downloaded securely, and stored locally, all outside of the knowledge of‬
‭the title. Once installed the game can see the new files and load them.‬

‭Should CRC checks be performed on Consoles?‬


‭When loading an AssetBundle the CRC should not be checked on a console.‬

‭ onsole CPUs take time to validate the CRC for a newly-opened bundle. Load times are very‬
C
‭important on consoles. On consoles with fast SSD storage, a CPU bound CRC check will slow‬
‭down loading.‬

‭ ecause AssetBundles are typically included in the title installation on local storage, or‬
B
‭downloaded securely with DLC mechanisms, there is no need to perform CRC checks. The‬
‭only case where it can be prudent is when doing a UnityWebRequestAssetBundle download, at‬
‭the time of download and not repeated at each load.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭49‬
‭What platforms do not support AssetBundle caching?‬
‭ he WebGL platform, along with some consoles, have AssetBundle caching turned off. This is‬
T
‭a compile time switch (‬‭ENABLE_CACHING‬‭) so it is not‬‭possible to enable it dynamically.‬

‭ n WebGL it was disabled in recent versions of Unity because there is a built-in download‬
O
‭cache that can be used to cache responses from requested URLs. The downside of this‬
‭approach is much more memory usage - AssetBundles are always copied fully into memory to‬
‭load them. If the file was compressed with LZMA this also includes an on-demand conversion‬
‭to LZ4 format.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭50‬
‭Related Technology and Other References‬

‭ hat is the relationship between AssetBundles and‬


W
‭Addressables?‬
‭ ddressables is an officially supported package from Unity that generates AssetBundles using‬
A
‭the Scriptable Build Pipeline. It offers convenient functionality for setting up AssetBundles and‬
‭for using them at runtime. See‬‭Addressable documentation‬‭for further information.‬

‭ uch of the details in this document about the content of AssetBundles remains true when‬
M
‭using Addressables. However the APIs/UI for building AssetBundles is different and a higher‬
‭level API is used to load the content of AssetBundles.‬

‭ hat is the relationship between AssetBundles and the Scriptable‬


W
‭Build Pipeline?‬
‭ he Scriptable Build Pipeline is an independent implementation of the code to build‬
T
‭AssetBundles. Most of its implementation is in the‬‭Scriptable Build Pipeline C# package‬‭, along‬
‭with some low-level APIs implemented directly in Unity to support it (for example for writing‬
‭serialized files).‬

I‭t produces the same file format as the “built-in” AssetBundle support, and offers an API similar‬
‭to [Link]() for developers who want to migrate their code to that‬
‭“pipeline”. But primary usage of the Scriptable Build Pipeline is from Addressables.‬

‭Are ".unitypackage” files a type of AssetBundle?‬


‭ o, the files created by the “Export package…” feature in Unity are [Link] files, using the‬
N
‭“.unitypackage” file extension. They have no relationship with the unity Archive file format or‬
‭AssetBundles.‬

‭Where to get more information about AssetBundles?‬


‭‬
● ‭ anual (AssetBundles)‬
M
‭●‬ ‭Scripting Reference:‬‭Script Reference ([Link])‬
‭●‬ ‭Official Tutorial‬‭Based on Unity 2017 but still largely‬‭valid‬
‭●‬ ‭Addressable documentation‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭51‬
‭Advanced Topics‬

‭ hy is it recommended to use the BuildSettings UI to set the‬


W
‭current target for an AssetBundle build?‬
‭ he [Link] API takes a target argument and this can be different than‬
T
‭the “current” target shown in the Build Settings. For example your target might be Windows64‬
‭but you can build AssetBundles for Android with your build script.‬

‭ e recommend using the Build Settings UI to change targets, then build based on the current‬
W
‭target. The reason for this is because switching current build target results in a recompilation of‬
‭the Editor scripts to reflect the new platform and a reload of the domain. That is done because‬
‭the Unity Editor attempts to simulate the target platform as closely as possible. The problem is‬
‭that domain reloads only happen after the current script finishes running, so you cannot reload‬
‭the mono domain in the middle of a build script. On the other hand if you change the target in‬
‭the UI then the domain will reload as part of the UI refresh and be properly loaded when you‬
‭then launch a build asset bundles script.‬

‭The only place this matters is‬


‭●‬ ‭Any code that compiles based on‬‭platform defines‬‭.‬ ‭For example during the build there‬
‭can be script code that runs in build callbacks and that code might be compiled‬
‭differently based on the platform.‬
‭●‬ ‭Any assemblies that are loaded based on the platform‬

‭ ften this subtle difference doesn’t matter, for example callback code could check the target‬
O
‭dynamically instead of using platform conditional compilation. So scripts that build for many‬
‭targets from a single script may work properly.‬

I‭n the case of command line builds there is a target argument available which should be used‬
‭so that Unity loads with the correct target that already matches the target passed to‬
‭[Link]. See also the note about command line builds in‬
‭[Link]‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭52‬
‭What is the AssetBundle Object?‬
‭ here is always a single‬‭AssetBundle object‬‭, always‬‭found in the first SerializedFile‬
T
‭For regular assetbundles it is local file id “1”‬
‭For scene assetbundles it is local file id “2” (the PreloadData object gets “1”), and sharedAssets‬
‭file of the first scene.‬

‭It looks like this in the Binary2Text output.‬

ID: 1 (ClassID: 142) AssetBundle‬



m_Name "[Link]" (string)‬

m_PreloadTable (vector)‬

…etc…‬

‭ he AssetBundle object is rather low level, but is used by the Load code to determine what‬
T
‭assets are available inside the assetbundle, and how to load them.‬

‭It contains:‬

‭‬ m
● ‭ _Name. Name of the AssetBundle.‬
‭●‬ ‭m_Container. List of all the “loadable” objects, e.g. Assets and “SubAssets”. Each entry‬
‭records:‬
‭○‬ ‭the name of the asset. The name is typically the project relative path of the‬
‭original asset.‬
‭○‬ ‭The root object itself (e.g. the LFID of another object within the same serialized‬
‭file, e.g. m_FileID=0)‬
‭○‬ ‭A range of the preload table, specifying all the objects that need to be preloaded‬
‭when the root object is loaded.‬
‭○‬ ‭Note: In the case of a scene bundle, the name is the Scenes path, and the root‬
‭object and preload table ranges are null.‬
‭●‬ ‭m_PreloadTable. A combined list of all objects needed by the entries in m_Container.‬
‭These can include objects outside of the SerializedFile (m_FileID > 0)‬
‭●‬ ‭m_IsStreamedSceneAssetBundle. Set to true if the AssetBundle contains scenes.‬
‭●‬ ‭m_MainAsset. Obsolete‬
‭●‬ ‭m_Dependencies‬
‭●‬ ‭…a few other flags and values‬

‭See the advanced section for more information and examples.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭53‬
‭How can I tell what objects can be loaded from an AssetBundle?‬
‭The API‬‭[Link]‬‭would be the‬‭typical way to list the contents.‬

‭ ut this section explains a bit of inner workings for how loading actually works and how you can‬
B
‭poke around a bit inside a Binary2Text output file.‬

‭ s mentioned earlier, the SerializedFile inside a regular AssetBundle always has a single‬
A
‭AssetBundle object that can be examined if you dump out the file to text format.‬

‭ hat object includes the‬‭m_Container‬‭map, which lists‬‭all the assets that can be loaded by‬
T
‭name and which object that corresponds to. For some assets there could be more than one‬
‭object that can be directly loaded (e.g. a “Main” object and additional “Visible” Objects)‬

‭ or example in the case of an AssetBundle containing a single Mesh, “[Link]”, the container‬
F
‭section might look like this:‬

‭D: 1 (ClassID: 142) AssetBundle‬


I
…‬

m_Container (map)‬

size 3 (int)‬

data (pair)‬

first‬‭
‭ "assets/meshes/[Link]"‬‭
(string)‬
second (AssetInfo)‬

preloadIndex 0 (int)‬

preloadSize 8 (int)‬

asset (PPtr<Object>)‬

m_FileID 0 (int)‬

m_PathID‬‭
‭ 2857099542465042866‬‭
(SInt64)‬
data (pair)‬

first‬‭
‭ "assets/meshes/[Link]"‬‭
(string)‬
second (AssetInfo)‬

preloadIndex 0 (int)‬

preloadSize 8 (int)‬

asset (PPtr<Object>)‬

m_FileID 0 (int)‬

m_PathID -‬
‭ 4910272969512289912‬‭
‭ (SInt64)‬
data (pair)‬

first‬‭
‭ assets/meshes/[Link]"‬‭
(string)‬
second (AssetInfo)‬

preloadIndex 0 (int)‬

preloadSize 8 (int)‬

asset (PPtr<Object>)‬

m_FileID 0 (int)‬

m_PathID‬‭
‭ 5228941712992785916‬‭
(SInt64)‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭54‬
‭ he m_PathID values reference other objects in the same file. In this case they correspond to‬
T
‭these objects:‬

‭D: 2857099542465042866 (ClassID: 1) GameObject‬


I
…‬

ID: -4910272969512289912 (ClassID: 43) Mesh‬

…‬

ID: 5228941712992785916 (ClassID: 21) Material‬

‭ his means that you can load any of those three objects directly (without actually needing to‬
T
‭write code that does anything with those path values):‬

‭ar gameObject = [Link]<GameObject>("[Link]");‬


v
var mesh = [Link]<Mesh>("[Link]");‬

var material = [Link]<Material>("[Link]");‬

‭ he SerializedFile also contains other objects, for example Shaders, MeshRenders etc. Those‬
T
‭are also available, but not directly from a LoadAsset<> call - instead you would reference them‬
‭from one of the main objects.‬

‭/ NULL!!!!‬
/
var renderer = [Link]<MeshRenderer>("[Link]");‬

‭/ This works‬
/
var gameObject = [Link]<GameObject>("[Link]");‬

var renderer = [Link]<MeshRenderer>();‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭55‬
‭What is a PreloadData object?‬
I‭n the Unity editor Objects are often loaded on-demand, by following references (Pptr) and using‬
‭info tracked inside the Persistent Manager to find and load the objects inside SerializedFiles.‬
‭However this is not very efficient IO-wise.‬

‭ o make loading within AssetBundles more efficient the build will calculate what objects are‬
T
‭needed to accompany each the root object for each loadable object. For example for a Prefab‬
‭the root is a GameObject and all the children GameObjects and components should also be‬
‭loaded together with it.‬

‭ he information about what objects to load at the same time is recorded in the‬
T
‭PreloadData.m_Assets list. This is a flat listing of all the Object references needed by any of‬
‭the loadable root objects. The AssetBundle m_Containers structure (mentioned in the previous‬
‭section) will reference a range of indexes within the PreloadData array. For example if an‬
‭AssetBundle contained two Prefabs then the objects for one Prefab would be listed first, then all‬
‭the objects needed in the second Prefab would be listed next.‬

‭ s an example, here is a binary2text dump from the sharedAssets file of a scene (which‬
A
‭references objects inside another scene’s sharedAsset file)‬

‭xternal References‬
E
path(1): "Resources/unity_builtin_extra" GUID:‬

0000000000000000f000000000000000 Type: 0‬

path(2): "Library/unity default resources" GUID:‬

0000000000000000e000000000000000 Type: 0‬

path(3):‬

"archive:/BuildPlayer-BakedLightsRealtime/[Link]‬

aredAssets" GUID: 00000000000000000000000000000000 Type: 0‬

ID: 1 (ClassID: 150) PreloadData‬



m_Name "" (string)‬

m_Assets (vector)‬

size 8 (int)‬

data (PPtr<Object>)‬

m_FileID 1 (int)‬

m_PathID 6 (SInt64)‬

data (PPtr<Object>)‬

m_FileID 2 (int)‬

m_PathID 10001 (SInt64)‬

data (PPtr<Object>)‬

m_FileID 2 (int)‬

m_PathID 10202 (SInt64)‬

data (PPtr<Object>)‬

m_FileID 2 (int)‬

m_PathID 10209 (SInt64)‬

data (PPtr<Object>)‬

m_FileID 3 (int)‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭56‬
m_PathID 3 (SInt64)‬

data
‭ (PPtr<Object>)‬
m_FileID 3 (int)‬

m_PathID 4 (SInt64)‬

data
‭ (PPtr<Object>)‬
m_FileID 3 (int)‬

m_PathID 5 (SInt64)‬

data
‭ (PPtr<Object>)‬
m_FileID 3 (int)‬

m_PathID 6 (SInt64)‬

m_Dependencies (vector)‬

size 0 (int)‬

m_ExplicitDataLayout 0 (bool)‬

‭ he PreloadData list can reference objects that exist in the same Serialized File (m_FileID==0)‬
T
‭or other Serialized Files (m_FileID > 0). When it is another SerializedFile then it is either a file‬
‭inside another AssetBundle or a built-in object.‬

‭ or Assets that contain SubAssets, then the same range of objects might be referenced multiple‬
F
‭times. For example for an FBX which can be loaded as a GameObject, Mesh or Material then‬
‭the objects created by the FBX importer will only appear once in the PreloadData and three‬
‭entries in the AssetBundle.m_Containers will reference the same range. Returning to the‬
‭example used in a previous section, notice how all three entries use range 0-7 of the preload‬
‭table.‬

‭D: 1 (ClassID: 142) AssetBundle‬


I
…‬

m_Container (map)‬

size 3 (int)‬

data (pair)‬

first‬‭
‭ "‭
a
‬ssets/meshes/[Link]‬
"‬‭
‭ (string)‬
second (AssetInfo)‬

preloadIndex 0 (int)‬

preloadSize 8 (int)‬

asset (PPtr<Object>)‬

m_FileID 0 (int)‬

m_PathID 2857099542465042866 (SInt64)‬

data (pair)‬

first‬‭
‭ "‭
a
‬ssets/meshes/[Link]‬
"‬‭
‭ (string)‬
second (AssetInfo)‬

preloadIndex 0 (int)‬

preloadSize 8 (int)‬

asset (PPtr<Object>)‬

m_FileID 0 (int)‬

m_PathID -4910272969512289912 (SInt64)‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭57‬
data
‭ (pair)‬
‭irst “assets/meshes/[Link]‬
f "‬‭
‭ (string)‬
second (AssetInfo)‬

preloadIndex 0 (int)‬

preloadSize 8 (int)‬

asset (PPtr<Object>)‬

m_FileID 0 (int)‬

m_PathID 5228941712992785916 (SInt64)‬

‭ ow do references between AssetBundles show up inside a‬


H
‭SerializedFile?‬

‭ o illustrate, this example is based on an AssetBundle (testbundle0) with a material that‬


T
‭references a shader inside another bundle, testbundle1.‬

‭The dependency between AssetBundles is visible inside the build folder .manifest file.‬

AssetBundleInfos:‬

Info_0:‬

Name: [Link]‬

Dependencies:‬

Dependency_0: [Link]‬

Info_1:‬

Name: [Link]‬

Dependencies: {}‬

‭And also available programmatically via‬‭[Link]‬

‭ ithin the SerializedFile text dumps it is possible to see the dependency at a much lower level,‬
W
‭down to the individual objects.‬

‭ or example the dump for testbundle0 has an external reference listed at the top of the‬
F
‭Binary2Text dump:‬

External References‬

‭ath(1):‬
p
"archive:/CAB-772d3b012d144d3fced8206712901558/CAB-772d3b012d144d3fce‬

d8206712901558" GUID: 00000000000000000000000000000000 Type: 0‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭58‬
‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬
‭59‬
‭ nfortunately the use of hashes for the names makes things a bit more obfuscated, e.g.‬
U
‭772d3b012d144d3fced8206712901558 is the hash of the string “[Link]”. The‬
‭syntax shows how the Unity virtual file system will mount the assetbundle when it is loaded. But‬
‭normally you shouldn’t need to calculate or understand all the defaults of these paths.‬

‭ his asset bundle reference is at path “1”, and the serialized data of the Material references that‬
T
‭file in m_Shader.m_FileID.‬

ID: 7395359030061443441 (ClassID: 21) Material‬



m_Name "BlueMaterial" (string)‬

m_Shader (PPtr<Shader>)‬

m_FileID 1 (int)‬

m_PathID‬‭
‭ 8028051458226250111‬‭
(SInt64)‬

‭ o we know that it depends on the shader with local file id (LFID)‬‭8028051458226250111‬‭inside‬


S
‭[Link].‬

‭ he AssetBundle object also records this information in a centralized way that Unity’s load code‬
T
‭can use.‬
ID: 1 (ClassID: 142) AssetBundle‬

m_Name "[Link]" (string)‬

….‬

m_Container (map)‬

size 1 (int)‬

data (pair)‬

first "‬
‭ assets/shadervariants/[Link]‬
‭ "‬‭
‭ (string)‬
second (AssetInfo)‬

preloadIndex 0 (int)‬

preloadSize 2 (int)‬

asset (PPtr<Object>)‬

m_FileID 0 (int)‬

m_PathID‬‭
‭ 7395359030061443441‬‭
(SInt64)‬

‭ he above data structure records that, when a load of “[Link]” is requested, then the‬
T
‭object 7395359030061443441 should be created.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭60‬
I‭t also records “preloadIndex 0, preloadSize 2”. That defines a range of elements in another list‬
‭that is called the m_PreloadTable, in this case both elements.‬

m_PreloadTable (vector)‬

size 2 (int)‬

data (PPtr<Object>)‬

m_FileID 1 (int)‬

m_PathID 8028051458226250111 (SInt64)‬

data (PPtr<Object>)‬

m_FileID 0 (int)‬

m_PathID 7395359030061443441 (SInt64)‬

‭ he preload information says that the external object 8028051458226250111 is needed. Using‬
T
‭that extra info Unity would be able to also load the shader as the material is required.‬

I‭n practice the m_PreloadTable and m_Container can be very large, because there can be‬
‭many assets and many associated objects that need to be loaded together.‬

‭How are script types represented inside AssetBundles?‬


‭ he MonoScript object is a UnityObject that represents a specific MonoBehaviour derived class‬
T
‭(or ScriptableObject, ScriptedImporter etc). When a MonoBehaviour is serialized in the Editor it‬
‭references the class via a regular unity reference to the MonoScript as tracked by the‬
‭AssetDatabase (e.g. GUID + LFID).‬

‭ hen building AssetBundles the MonoScript objects must be included in the generated‬
W
‭SerializedFiles. This is where the actual assembly, namespace and class name are recorded.‬

‭ onoScripts are lightweight objects. Sometimes the same MonoScript may be duplicated in‬
M
‭different AssetBundles, or they may be shared by reference from one AssetBundle to another.‬
‭For example if the MonoBehaviour references a MonoScript in another AssetBundle then the‬
‭reference records the SerializedFile inside another AssetBundle as well as the LFID for the‬
‭MonoScript object in that file.‬

‭ he behavior for whether MonoScripts are duplicated or shared between bundles varies‬
T
‭depending on the build pipeline implementation. The Scriptable Build Pipeline (and‬
‭Addressables) supports an optional feature to create a special AssetBundle with all the‬
‭MonoScripts accumulated in a single dedicated bundle.‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭61‬
‭Appendix:‬

‭How to read the header sample code‬


‭sing [Link];‬
u
using [Link];‬

‭ublic class ArchiveHeaderData‬


p
{‬

public string Signature;‬

public int Version;‬

public string UnityWebBundleVersion;‬

public string UnityWebMinimumRevision;‬

public long Size;‬

public int CompressedBlocksInfoSize;‬

public int UncompressedBlocksInfoSize;‬

public int Flags;‬

public static ArchiveHeaderData CreateFromStream(Stream stream)‬

{‬

ArchiveHeaderData h = new ArchiveHeaderData();‬

BinaryReader reader = new BinaryReader(stream);‬

[Link] = [Link](reader);‬

[Link] = SerializationUtility.ReadInt32BigEndian(reader);‬

[Link] = [Link](reader);‬

[Link] = [Link](reader);‬

[Link] = SerializationUtility.ReadInt64BigEndian(reader);‬

[Link] = SerializationUtility.ReadInt32BigEndian(reader);‬

[Link] = SerializationUtility.ReadInt32BigEndian(reader);‬

[Link] = SerializationUtility.ReadInt32BigEndian(reader);‬

return h;‬

}‬

public static ArchiveHeaderData CreateFromFile(string path)‬

{‬

using (FileStream f = [Link](path))‬

{‬

return [Link](f);‬

}‬

}‬

}‬

‭lass SerializationUtility‬
c
{‬

public static string ReadNullTerminatedString(BinaryReader r)‬

{‬

StringBuilder builder = new StringBuilder();‬

while (true)‬

{‬

byte b = [Link]();‬

if (b == 0)‬

break;‬

[Link]((char)b);‬

}‬

return [Link]();‬

}‬

public static int ReadInt32BigEndian(BinaryReader r)‬

{‬

byte[] b = [Link](4);‬

[Link](b);‬

return [Link].ToInt32(b);‬

}‬

public static long ReadInt64BigEndian(BinaryReader r)‬

{‬

byte[] b = [Link](8);‬

[Link](b);‬

return [Link].ToInt64(b);‬

}‬

}‬

‭UNITY CONFIDENTIAL - DO NOT DISTRIBUTE‬


‭62‬

You might also like