AssetBundles are a collection of assets, packaged for loading at runtime. With Asset Bundles, you can dynamically load and unload new content into your application. AssetBundles can be used to implement post-release DLC.
They can be used to reduce the amount of space on disk used by your game, when first deployed. It can also be used to add new content to an already published game.
To create an AssetBundle you need to use the BuildPipeline editor class. All scripts using Editor classes must be placed in a folder named Editor, anywhere in the Assets folder. Here is an example of such a script in C#:
using UnityEngine;
using UnityEditor;
public class ExportAssetBundles {
[MenuItem("Assets/Build AssetBundle")]
static void ExportResource () {
string path = "Assets/myAssetBundle.unity3d";
Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets);
}
}
}
An Editor script does not need to be applied to a GameObject, it is instead used by the Editor. This previous example will create a new item in the “Assets” menu of your editor called “Build AssetBundle”.
To use this example:
The BuildAssetBundle function is the line that creates the AssetBundle and saves it to the specified location. The first parameter specifies the mainAsset, which is a special Asset that can be obtained directly with the mainAsset property when loading Assets from the AssetBundle. It is not mandatory to set a main Asset, if this is not going to be used you can use null for the parameter. The second parameter is the array of objects that will make up the AssetBundle. The third parameter is the location on disk that the AssetBundle will be saved to. The final parameter are the build flags or options used when building AssetBundles. The bitwise OR ( ‘|’ ) combines the options passed to build the AssetBundles. In this example BuildAssetBundleOptions.CollectDependencies sets it to include other Assets referenced by the objects and BuildAssetBundleOptions.CompleteAssets make sure that assets are completely included.
Building AssetBundles should be a pre-publish step which happens only once and with a single function call, for example, with a Menu Item that builds all the AssetBundles. As you develop your application you should write helper scripts that can build all your AssetBundles for a target platform with a single click or in batchmode without user intervention.
There are also other function to create AssetBundles. You can read more about this here.
There are two main steps involved when working with AssetBundles. The first step is to download the AssetBundle from a server or disk location. This is done with the WWW class. The second step is to load the Assets from the AssetBundle, to be used in the application. Here is an example C# script:
using UnityEngine;
using System.Collections;
public class BundleLoader : MonoBehaviour{
public string url;
public int version;
public IEnumerator LoadBundle(){
using(WWW www = WWW.LoadFromCacheOrDownload(url, version){
yield return www;
AssetBundle assetBundle = www.assetBundle;
GameObject gameObject = assetBundle.mainAsset as GameObject;
Instantiate(gameObject );
assetBundle.Unload(false);
}
}
void Start(){
StartCoroutine(LoadBundle());
}
}
This script is added to a GameObject as Component. The AssetBundle is loaded in the following way:
Please note that for simplicity the previous example is not doing any safety checks. Please look at the code here for a more complete example.
As creating applications is an iterative process, you will very likely modify your Assets many times, which would require rebuilding the AssetBundles after every change to be able to test them. Even though it is possible to load AssetBundles in the Editor, that is not the recommended workflow. Instead, while testing in the Editor you should use the helper function Resources.LoadAssetAtPath to avoid having to use and rebuild AssetBundles. The function lets you load the Asset as if it were being loaded from an AssetBundle, but will skip the building process and your Assets are always up to date.
The following is an example helper script, that you can use to load your Assets depending on if you are running in the Editor or not. Put this code in C# script named AssetBundleLoader.cs:
using UnityEngine;
using System.Collections;
public class AssetBundleLoader {
public Object Obj; // The object retrieved from the AssetBundle
public IEnumerator LoadBundle<T> (string url, int version, string assetName, string assetPath) where T : Object {
Obj = null;
#if UNITY_EDITOR
Obj = Resources.LoadAssetAtPath(assetPath, typeof(T));
if (Obj == null)
Debug.LogError ("Asset not found at path: " + assetPath);
yield break;
#else
WWW download;
if ( Caching.enabled ) {
while (!Caching.ready)
yield return null;
download = WWW.LoadFromCacheOrDownload( url, version );
}
else {
download = new WWW (url);
}
yield return download;
if ( download.error != null ) {
Debug.LogError( download.error );
download.Dispose();
yield break;
}
AssetBundle assetBundle = download.assetBundle;
download.Dispose();
download = null;
if (assetName == "" || assetName == null)
Obj = assetBundle.mainAsset;
else
Obj = assetBundle.Load(assetName, typeof(T));
assetBundle.Unload(false);
#endif
}
}
We can now use the AssetBundleLoader script to load an Asset from an AssetBundle if we are running the built application or load the Asset directly from the Project folder if running in the Editor:
using UnityEngine;
using System.Collections;
public class ExampleLoadingBundle : MonoBehaviour {
public string url = "http://www.mygame.com/myBundle.unity3d"; // URL where the AssetBundle is
public int version = 1; // The version of the AssetBundle
public string assetName = "MyPrefab"; // Name of the Asset to be loaded from the AssetBundle
public string assetPath; // Path to the Asset in the Project folder
private Object ObjInstance; // Instance of the object
void Start(){
StartCoroutine(Download());
}
IEnumerator Download () {
AssetBundleLoader assetBundleLoader = new AssetBundleLoader ();
yield return StartCoroutine(assetBundleLoader.LoadBundle <GameObject> (url, version, assetName, assetPath));
if (assetBundleLoader.Obj != null)
ObjInstance = Instantiate (assetBundleLoader.Obj);
}
void OnGUI(){
GUILayout.Label (ObjInstance ? ObjInstance.name + " instantiated" : "");
}
}
The previous script should be saved to a file named ExampleLoadingBundle.cs inside the Assets folder. After setting the public variables to their correct values and running it, it will use the AssetBundleLoader class to load an Asset. It is then instantiated and this will be shown by using the GUI.
You can use WWW.LoadFromCacheOrDownload which automatically takes care of saving your AssetBundles to disk. Be aware that on the Webplayer you are limited to 50MB in total (shared between all webplayers). You can buy a separate caching license for your game if you require more space.
AssetBundles are compatible between some platforms. Use the following table as a guideline.
Platform compatibility for AssetBundles | |||||
Standalone | Webplayer | iOS | Android | ||
Editor | Y | Y | Y | Y | |
Standalone | Y | Y | |||
Webplayer | Y | Y | |||
iOS | Y | ||||
Android | Y |
For example, a bundle created while the Webplayer build target was active would be compatible with the editor and with standalone builds. However, it would not be compatible with apps built for the iOS or Android platforms.
When you build AssetBundles the assets are identified internally by their filename without the extension. For example a Texture located in your Project folder at “Assets/Textures/myTexture.jpg” is identified and loaded using “myTexture” if you use the default method. You can have more control over this by supplying your own array of ids (strings) for each object when Building your AssetBundle with BuildPipeline.BuildAssetBundleExplicitAssetNames.
AssetBundles allow you to share content between different games. The requirement is that any Assets which are referenced by GameObjects in your AssetBundle must be included in the AssetBundle. To make sure the referenced Assets are included in the AssetBundle when they are built you can pass the BuildAssetBundleOptions.CollectDependencies option.
AssetBundles can contain a structure called a type tree which allows information about asset types to be understood correctly between different versions of Unity. On desktop platforms, the type tree is included by default but can be disabled by passing the BuildAssetBundleOptions.DisableWriteTypeTree to the BuildAssetBundle function. Webplayers intrinsically rely on the type tree and so it is always included (ie, the DisableWriteTypeTree option has no effect). Type trees are never included for mobile and console asset bundles and so you will need to rebuild these bundles whenever the serialization format changes. This can happen in new versions of Unity. (Except for bugfix releases) It also happens if you add or remove serialized fields in monobehaviour’s that are included in the asset bundle. When loading an AssetBundle Unity will give you an error message if the AssetBundle must be rebuilt.
You can use AssetBundle.LoadAll to retrieve an array containing all objects from the AssetBundle. It is not possible to get a list of the identifiers directly. A common workaround is to keep a separate TextAsset to hold the names of the assets in the AssetBundle.