Asset Bundle Usage in Unity 3D

NSDG | Nov 27, 2019 | 0 Comments
Asset Bundle Usage in Unity 3D
155

In this post I will be covering all the aspects related to the Asset Bundle usage in Unity 3D. Specifically I will be covering the following points:

  • What are Asset Bundles?
  • Why to use Asset Bundles in your Game?
  • Exporting Asset Bundles
  • Importing Asset Bundles

What are Asset Bundles?

Asset Bundles are files that contain the game assets, from a simple assets such as 3D Models, Textures, Audio Clips to a more complex such as Scenes.

Scripts cannot be included in Asset Bundles, only their references, so be careful when renaming or moving the scripts as it will break the connection and you'll have to rebuild the Asset Bundles to make them work again.

Why to use Asset Bundles in your Game?

Normally when building the game in Unity, all the assets get included in the player. When the game size is relatively small (under 50MB) it does not pose any issues (players can download it rather fast), but imagine your game size is in triple digits (ex. 300MB) or higher. Users would still be able to download it relatively fast with a modern internet, but having to re-download the whole game whenever you release updates will quickly add up. That's where Asset Bundles come in handy, instead of including all the assets in the player, you pack them into Asset Bundles. Players only need to download them once (unless you explicitly instruct to re-download them), then they get saved in cache.

Exporting Asset Bundles

Exporting Asset Bundles is done in two steps: assigning Asset Bundle names, then building the Asset Bundles using Editor script.

Assigning Asset Bundle names:

Assigning Asset Bundle name is done by selecting the asset in the Project view (this could be Prefab, Texture or even Scene) then in the Inspector view at the very bottom click on dropdown menu near AssetBundle then click New... (or click on the existing Asset Bundle name to attribute this asset to it).

Assigning the same bundle name to multiple assets will pack them together in the same Asset Bundle. It's advised to pack Scenes separately from the rest of the assets.

Also you don't have to assign AssetBundle name to every asset. Typically you only need to assign the bundle name to the main prefab or asset, the rest of the dependencies will be included automatically.

Building the Asset Bundles:

  • Create a new folder called Editor (if you do not have any)
  • Create new script inside the Editor folder, name it BuildAssetBundles then paste the code below inside it:

BuildAssetBundles.cs

using UnityEngine;
using UnityEditor;

public class BuildAssetBundles
{
    [MenuItem("Build/Build AssetBundles")]
    static void BuildAllAssetBundles()
    {
        string outputFolder = "Assets/__Bundles";

        //Check if __Bundles folder exist
        if (!AssetDatabase.IsValidFolder(outputFolder))
        {
            Debug.Log("Folder '__Bundles' does not exist, creating new folder");

            AssetDatabase.CreateFolder("Assets", "__Bundles");
        }

        BuildPipeline.BuildAssetBundles(outputFolder, BuildAssetBundleOptions.ChunkBasedCompression, EditorUserBuildSettings.activeBuildTarget);
    }
}

After saving it you'll notice it will add a menu button (Build -> Build AssetBundles). Clicking it will build the Asset Bundles and place them in "__Bundles" folder.

Importing Asset Bundles

To import AssetBundle we first need to download it using UnityWebRequest then Load the AssetBundle using special Function to be able use it in the game. Generally there are 2 types of Asset Bundles, those that contain assets and those that contain Scenes.

Load Assets from Asset Bundle:

The code below downloads Asset Bundle named "fpsplayer" then extracts the Prefab named "FPSPlayer" and instantiates it in Scene:

        int assetBundleVersion = 1; // Changing this number will force Asset Bundle reload
        string assetBundlePath = "file://" + Application.dataPath + "/__Bundles/" + "fpsplayer"; // Path to Asset Bundle file
        using (UnityEngine.Networking.UnityWebRequest www = UnityEngine.Networking.UnityWebRequestAssetBundle.GetAssetBundle(assetBundlePath, (uint)assetBundleVersion, 0))
        {
            yield return www.SendWebRequest();

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.LogError("AssetBundle Error: " + www.error);
                yield return null;
            }
            else
            {
                // Get downloaded Asset Bundle
                AssetBundle assetBundle = UnityEngine.Networking.DownloadHandlerAssetBundle.GetContent(www);
                // Extract Prefab named "FPSPlayer" from the Asset Bundle
                GameObject playerPrefab = assetBundle.LoadAsset("FPSPlayer") as GameObject;
                // Instantiate Player Prefab
                Instantiate(playerPrefab, Vector3.zero, Quaternion.identity);
                // Unload Asset Bundle from memory (but do not destroy the existing instance(s))
                assetBundle.Unload(false);
            }
        }

Load Scene from Asset Bundle:

Loading Scene from Asset Bundle is slightly different.

The code below will download the Asset Bundle with a Scene and will make it available for load:

        int assetBundleVersion = 1; // Changing this number will force Asset Bundle reload
        string assetBundlePath = "file://" + Application.dataPath + "/__Bundles/" + "testscene"; // Path to Asset Bundle file
        using (UnityEngine.Networking.UnityWebRequest www = UnityEngine.Networking.UnityWebRequestAssetBundle.GetAssetBundle(assetBundlePath, (uint)assetBundleVersion, 0))
        {
            yield return www.SendWebRequest();

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.LogError("AssetBundle Error: " + www.error);
                yield return null;
            }
            else
            {
                // Get downloaded Asset Bundle (This will make the Scene available for load)
                AssetBundle assetBundle = UnityEngine.Networking.DownloadHandlerAssetBundle.GetContent(www);
                // Load the Scene extracted from the Asset Bundle
                UnityEngine.SceneManagement.SceneManager.LoadScene("TestScene");
            }
        }