How To Implement AssetBundles in Unity3D

Implement AssetBundles In Unity3D

In this blog post, We try to explain How to Get data from AssetBundle on The Server and Data From AssetBundle In Local.

What Is An AssetBundle?

An AssetBundle is a collection of assets from a project saved in a compact file with the purpose of being loaded separately to the application. AssetBundles can be loaded on demand by a game or application built in Unity. This allows streaming and asynchronous loading of content such as models, textures, audio clips, or even entire scenes. AssetBundles can be “pre-cached” and stored locally for immediate loading when first running an application. AssetBundles should be built for each target platform separately.

What Is The Use Of It?

AssetBundles can be used for downloadable content (DLC), reducing initial install size, loading assets optimized for the end-users platform, and reduce runtime memory pressure. The primary purpose of AssetBundles, however, is to stream content on demand from a remote location, to be loaded into the application as necessary. AssetBundles can contain any kind of asset type recognized by Unity, including custom binary data. The only exception is that script assets are not allowed.

In this tutorial, we will cover following topics for Android and iOS platform:

1

How to get data from asset bundle in server

2

How to get data from asset bundle in local

#1

How To Get Data From AssetBundle On The Server?

Step-1: Build An AssetBundle

using UnityEngine;
using UnityEditor;

public class ExportAssetBundles
{

   [MenuItem ("Assets/Build AssetBundle")]
   static void ExportResource ()
   {
      // Bring up save panel
      string basename = Selection.activeObject ? 
                   Selection.activeObject.name : "New Resource";
      string path = EditorUtility.SaveFilePanel ("Save Resources", 
              "", basename, "");

      if (path.Length != 0)
      {
         // Build the resource file from the active selection.
         Object[] selection = Selection.GetFiltered (typeof(Object), 
                              SelectionMode.DeepAssets);

         #if UNITY_ANDROID
         // for Android
         BuildPipeline.BuildAssetBundle (Selection.activeObject, 
                   selection, path + ".android.unity3d", 
                   BuildAssetBundleOptions.CollectDependencies |
         BuildAssetBundleOptions.CompleteAssets, BuildTarget.Android);
            
         #elif UNITY_IPHONE
         // for iPhone
         BuildPipeline.BuildAssetBundle (Selection.activeObject, 
                            selection, path + ".iphone.unity3d", 
              BuildAssetBundleOptions.CollectDependencies |
            BuildAssetBundleOptions.CompleteAssets, BuildTarget.iOS);
         #endif

         Selection.objects = selection;
        }
    }

}
ExportAssetBundles

Now, right click on the SoundAsset and click on the Build AssetBundle. Choose the location to save the AssetBundle and save it. The asset bundle will be available on the location for Android and as well as iOS. BuildPipeline.BuildAssetBundle will build the selected AssetBundle for the platform specified in BuildTarget. Now upload the AssetBundle to the Server.

To make an AssetBundle, first, we need to build the asset of unity project.You can manually mark an asset as AssetBundle. Select any asset from project view and in the bottom, part from model view give AssetBundle name. You can also set version of AssetBundle.

In this tutorial, we will load an asset that contains sound files. The project contains a SoundAsset prefab inside Assets folder. We will use this to create an AssetBundle, Load it into the project and play the sound.

Create a script “ExportAssetBundles” inside Editor folder and add following lines of code in it:

Step-2: Load AssetBundle From The Server

We will load the SoundAsset from the server and play the Audio clip attached to it.
Create a new script name it “AssetBundleSample ”. It will manage all the loading operation of asset bundle. Add the following lines code in it:

using System;
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.IO;

public class AssetBundleSample : MonoBehaviour
{
    public string loadUrl;
    public Text Statustext;
    bool isLoading;
    GameObject AssetBundleObj;
    
    // Use this for initialization
    void Start ()
    {
        isLoading = false;
    }
  
    // Update is called once per frame
    void Update ()
    {
        // progress
        if (isLoading) {
            int percent = (int)(www.progress * 100);
            Statustext.text = percent.ToString () + "%";
        }
    }

    private WWW www;

    private IEnumerator load (string soundAsset)
    {
        // wait for the caching system to be ready
        while (!Caching.ready)
            yield return null;

        // load AssetBundle file from Cache if it exists with the same        version or download and store it in the cache
        www = WWW.LoadFromCacheOrDownload (loadUrl, 1);
        yield return www;

        Debug.Log ("Loaded ");
        Statustext.text = "Loaded";
        if (www.error != null)
            throw new Exception ("WWW download had an error: " 
                                 + www.e rror);
    
        AssetBundle assetBundle = www.assetBundle;
        //Instantiate (assetBundle.mainAsset); 
        AssetBundleObj = Instantiate (assetBundle.mainAsset) as 
                         GameObject;
        AssetBundleObj.transform.Find (soundAsset).
                         GetComponent<AudioSource> ().Play ();
      //Unload the AssetBundles compressed contents to conserve memory
        assetBundle.Unload (false);
    }

    public void LoadSoundFiles (string soundAsset)
    {
        isLoading = true;
        // Clear Cache
        Caching.CleanCache ();

        if (AssetBundleObj) {
            Destroy (AssetBundleObj);
        }
        StartCoroutine (load (soundAsset));
        
    }
}
AssetBundleSample

Attach this script to an empty game object. create a Text to display loading status and assign to this script. Also, give the server URL of AssetBundle.

Now create 3 buttons and add LoadSoundFiles() method in the OnClick listener. Pass “Music1” as a parameter in button1, ”Music2” as a parameter in button2 and ”Music2” and “Music3 as a parameter in button3”. Now hit play. The loading will display as “%” format. when done with loading, it will first instantiate the AssetBundle and then will play the Audio Clip attached to it.

#2

How To Get Data From AssetBundle In Local?

In this example, we will create a scene as Asset bundle and load it from local memory. Create a scene and name it “AssetScene”. Add some images and other game objects to this scene you want.

Step-1: Build A Scene As AssetBundle

To create a scene as asset bundle, update the following lines of code to the “ExportAssetBundles” script:

using UnityEngine;
using UnityEditor;

public class ExportAssetBundles
{

  [MenuItem ("Assets/Build AssetBundle")]
  static void ExportResource ()
  {
     // Bring up save panel
    string basename = Selection.activeObject ? 
                Selection.activeObject.name : "New Resource";
    string path = EditorUtility.SaveFilePanel ("Save Resources", 
             "", basename, "");

    if (path.Length != 0)
    {
      // Build the resource file from the active selection.
       Object[] selection = Selection.GetFiltered (typeof(Object) 
                         , SelectionMode.DeepAssets);

       #if UNITY_ANDROID
       // for Android
       BuildPipeline.BuildAssetBundle (Selection.activeObject, 
                    selection, path + ".android.unity3d", 
             BuildAssetBundleOptions.CollectDependencies |
      BuildAssetBundleOptions.CompleteAssets, BuildTarget.Android);
            
      #elif UNITY_IPHONE
      // for iPhone
     BuildPipeline.BuildAssetBundle (Selection.activeObject, 
                     selection, path + ".iphone.unity3d", 
             BuildAssetBundleOptions.CollectDependencies |
            BuildAssetBundleOptions.CompleteAssets, BuildTarget.iOS);
     #endif

        Selection.objects = selection;
      }
   }

   [MenuItem ("Assets/Build SceneBundle")]
   static void ExportScene ()
   {
     if (!System.IO.Directory.Exists (Application.dataPath + 
                            "/AssetBundle"))
     {
       System.IO.Directory.CreateDirectory (Application.dataPath +
                               "/AssetBundle");
     }

     #if UNITY_ANDROID
     // for Android
     string[] levels = new string[] { "Assets/AssetScene.unity" };
     BuildPipeline.BuildStreamedSceneAssetBundle (levels, 
Application.dataPath + "/AssetBundle/AssetScene.unity3d", 
BuildTarget.Android);

     #elif UNITY_IPHONE
     // for iPhone
     string[] levels1 = new string[] { "Assets/AssetScene.unity" };
     BuildPipeline.BuildStreamedSceneAssetBundle (levels, 
Application.dataPath + "/AssetBundle/AssetScene.unity3d", 
BuildTarget.iOS);
     #endif
    }
}

This script will allow us to create an asset bundle from the scene. Right click on AssetScene in project view and click Build Scene Bundle. It will create an asset Bundle inside Assets/AssetBundle folder.

Step-2: Load AssetBundle From The Local

To load any files from local memory in any platform, first, we need to save it somewhere in the local machine. Upload this AssetScene to the server.

Update the AssetBundleSample Script with following lines of code:

using System;
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.IO;

public class AssetBundleSample : MonoBehaviour
{
    public string loadUrl;
    public Text Statustext;
    bool isLoading;
    GameObject AssetBundleObj;
    public Button LoadSceneButton;

    // Use this for initialization
    void Start ()
    {
        isLoading = false;
        LoadSceneButton.gameObject.SetActive (false);
        StartCoroutine (LoadSceneBundle ());
    }
  
    // Update is called once per frame
    void Update ()
    {
        // progress
        if (isLoading)
        {
            int percent = (int)(www.progress * 100);
            Statustext.text = percent.ToString () + "%";
        }
    }

    private WWW www;

    private IEnumerator load (string soundAsset)
    {
        // wait for the caching system to be ready
        while (!Caching.ready)
            yield return null;

        // load AssetBundle file from Cache if it exists with the same 		version or download and store it in the cache
        www = WWW.LoadFromCacheOrDownload (loadUrl, 1);
        yield return www;
   Statustext.text = "Loaded";
   if (www.error != null)
      throw new Exception ("WWW download had an error: "
 + www.error);
    
 AssetBundle assetBundle = www.assetBundle;
 //Instantiate (assetBundle.mainAsset); // Instantiate(assetBundle.Load("AssetName"));
AssetBundleObj=Instantiate (assetBundle.mainAsset) as GameObject;
AssetBundleObj.transform.Find (soundAsset).GetComponent<AudioSource> ().Play ();
// Unload the AssetBundles compressed contents to conserve memory
        assetBundle.Unload (false);
}

 public void LoadSoundFiles (string soundAsset)
 {
    isLoading = true;
    // Clear Cache
    Caching.CleanCache ();

     if (AssetBundleObj)
     {
         Destroy (AssetBundleObj);
     }
     StartCoroutine (load (soundAsset));
        
  }

  public void LoadFromLocal ()
  {
        string path = "";

        path = "file://" + Application.persistentDataPath + 
               "/AssetScene.unity3d";
        Debug.Log (path);

        StartCoroutine (LoadFromMemoryAsync (path));
    }

    IEnumerator LoadSceneBundle ()
    {
        Statustext.text = "Downloading scene ...";
        string SceneURL = "https://www.dropbox.com/s/fo4v777xvgrakrh/AssetScene.unity3d?dl=1";

        www = new WWW (SceneURL);
        yield return www;

        Debug.Log ("Loaded ");
        if (www.error != null)
            throw new Exception ("WWW download had an error: " + www.error);

        File.WriteAllBytes (Application.persistentDataPath + 
                      "/AssetScene.unity3d", www.bytes);
        yield return new WaitForEndOfFrame ();

        Statustext.text = "";

        LoadSceneButton.gameObject.SetActive (true);
    }

    IEnumerator LoadFromMemoryAsync (string path)
    {
        var LoadedAssetScene = AssetBundle.LoadFromFile 
   (Application.persistentDataPath + "/AssetScene.unity3d");
        if (LoadedAssetScene == null)
        {
            Debug.Log ("Failed to load AssetBundle!");
            yield return new WaitForEndOfFrame ();
        }
        UnityEngine.SceneManagement.SceneManager.LoadScene 
                                        ("AssetScene");
        LoadedAssetScene.Unload (false);
    }
}

Create a button name it Loads scene and adds LoadFromLocal() method in the OnClick listener. LoadSceneBundle will load the Scene Asset from the server and store it in the local memory of the device. Call this coroutine in Start() method.

Now when you click on LoadScene button, you will be navigated to the SceneBundle. You do not need to add this scene to build setting.

Assets-Bundle
Ketan Jogadiya
Ketan is a Unity3D game developer at Nimblechapps, a Unity 2D/3D game development company based in India. He is passionate about new technologies in mobile games. He is game developer by day and writer by night.
  • Steve Ricci

    Hi Ketan,

    Thank you very much for sharing your code! Did you ever update this code using the new Caching system and the Asset Bundle Manifest file?

    Thanks in advance,

    SR