Build and Load Assetbundles in Unity

Build and load Assetbundles in Unity

In this example below, I will demonstrate how to add new asset called "dog" to our AssetBundle named "animals" and build it then load it during run-time.

Setting Up Build Folders:

1. Select the asset such as image file. In this case, that's the "dog.jpeg" file. See the menu in the "Inspector" tab. Sometimes, the AssetBundle option it is hidden, drag it up to show it. See the animated gif below for how to do this. The default AssetBundle is "None". Click on the "None" option then go to the "New" option and create new AssetBundle and name it "animals"

Sample Image

2. Create a folder named StreamingAssets in the Assets folder. This is the folder we are going to build the AssetBundle into. Spelling counts and it's case sensitive so make sure to name it correctly.

Sample Image

3. Create sub-folder in the StreamingAssets folder to hold the AssetBundle. For this example, name this folder AssetBundles so that you can use it to recognize what's in it.

Sample Image


Building AssetBundle:

4. Below is the build script.

A. Create a script named ExportAssetBundles and put it in a folder named "Editor" in the Assets folder then copy the code below inside it:

using System.IO;
using UnityEditor;
using UnityEngine;

public class ExportAssetBundles
{
[MenuItem("Assets/Build AssetBundle")]
static void ExportResource()
{
string folderName = "AssetBundles";
string filePath = Path.Combine(Application.streamingAssetsPath, folderName);

//Build for Windows platform
BuildPipeline.BuildAssetBundles(filePath, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);

//Uncomment to build for other platforms
//BuildPipeline.BuildAssetBundles(filePath, BuildAssetBundleOptions.None, BuildTarget.iOS);
//BuildPipeline.BuildAssetBundles(filePath, BuildAssetBundleOptions.None, BuildTarget.Android);
//BuildPipeline.BuildAssetBundles(filePath, BuildAssetBundleOptions.None, BuildTarget.WebGL);
//BuildPipeline.BuildAssetBundles(filePath, BuildAssetBundleOptions.None, BuildTarget.StandaloneOSX);

//Refresh the Project folder
AssetDatabase.Refresh();
}
}

Sample Image

B. Build your AssetBudle by going to Assets --> Build AssetBundle menu.

You should see the built AssetBundles inside the Assets/StreamingAssets/AssetBundles directory. If not, refresh the Project tab.

Sample Image


Loading the AssetBundle during run-time:

5. When loading it, Application.streamingAssetsPath should be used to access the StreamingAssets folder. To access all the folders use, Application.streamingAssetsPath + "/AssetBundle/" + assetbunlenameWithoutExtension;. The AssetBundle and AssetBundleRequest API are used to load the AssetBundle. Since this is an image, Texture2D is passed to them. If using a prefab, pass GameObject instead then instantiate it. See comment in code for where these changes should be made. It is recommended to use Path.Combine to combine path names so the code below should use that instead.

Below is a simple loading function:

IEnumerator LoadAsset(string assetBundleName, string objectNameToLoad)
{
string filePath = System.IO.Path.Combine(Application.streamingAssetsPath, "AssetBundles");
filePath = System.IO.Path.Combine(filePath, assetBundleName);

//Load "animals" AssetBundle
var assetBundleCreateRequest = AssetBundle.LoadFromFileAsync(filePath);
yield return assetBundleCreateRequest;

AssetBundle asseBundle = assetBundleCreateRequest.assetBundle;

//Load the "dog" Asset (Use Texture2D since it's a Texture. Use GameObject if prefab)
AssetBundleRequest asset = asseBundle.LoadAssetAsync<Texture2D>(objectNameToLoad);
yield return asset;

//Retrieve the object (Use Texture2D since it's a Texture. Use GameObject if prefab)
Texture2D loadedAsset = asset.asset as Texture2D;

//Do something with the loaded loadedAsset object (Load to RawImage for example)
image.texture = loadedAsset;
}

Things to before loading note:

A. Name of Assetbundle is animals.

B. Name of the asset/object we want to load from the animals Assetbundle is dog This is a simple jpg of a dog.

Sample Image

C. The loading is simple as this:

string nameOfAssetBundle = "animals";
string nameOfObjectToLoad = "dog";

public RawImage image;

void Start()
{
StartCoroutine(LoadAsset(nameOfAssetBundle, nameOfObjectToLoad));
}

How can i load and instantiate any game object from assetbundle?


1- how can I load main asset with new assetbundle unity system with my assetbundles have any type of game object like : sprite, obj, sound, video and etc.

You would have to use a generic function, eg:

MyAssetHelper {
public static T Load<T>(string bundle, string asset) {
return SomeT;
}
}

We'll have to finish writing this function, and its not going to return a method like that, but for the moment just think of it as a method that requests a given asset from a given bundle and all you care is that you get something back.

2- I do not know how I can instantiate my loaded asset?

We're going to do this by modifying our function to take two more parameters, OnLoad which we want to have happen when the asset we want is finally in memory and available, and OnLoadFailed which gets called when oops, something bad happened. We don't know how long it will take to load the bundle and/or load the asset, so we have to wait and rely on the callbacks.

MyAssetHelper {
public static void Load<T>(string bundle, string asset, UnityAction<T> OnLoad, UnityAction OnLoadFailed) where T : UnityEngine.Object {
T obj;
//get the object somehow
if(obj != null) OnLoad.Invoke(obj); //we have an object
else OnLoadFailed.Invoke(); //something failed!
}
}

Now you'll have to specify what type you want when you call the function (which you needed before anyway, but you were always using GameObject) as well as callback functions when things are loaded (or they fail).

So, an example of how to call this:

public void LoadASprite() { //arbitrary function, could be Start
MyAssetHelper.Load<Sprite>( //the above function in a static class
"bundle","sprite_asset", sprite => { //anonymous lambda expression (success)
Debug.Log("We loaded a sprite! " + sprite);
}, () => { //anonymous lambda expression (failure)
Debug.Log("Failed to load sprite!");
}
);
}

Depending on what type you're loading you'd need to do different things with it, Instantiating it, assigning it to a field, whatever. On failure, you probably want to print some kind of message. In runtime you may either want to assume that the bundle always succeeds ("I know that I'll be shipping them together") or display a message to the user ("Failed to download bundle, check your connection"). Its up to you. You could even make the OnLoadFail function take a string parameter so you can provide more information (as we're going to call it from multiple places).

Now, though, we have to finish writing out our actual load method. We've templated out what it needs to look like (takes in a bundle name, an asset name, and has two callback functions), but we still need to do the actual loading.

We know it isn't going to return right away (that's why we created callbacks), so we need to move our contents off to a new method, which will be a coroutine, so we need to start that coroutine.

MyAssetHelper {
public static void Load<T>(string bundle, string asset, UnityAction<T> OnLoad, UnityAction OnLoadFailed) where T : UnityEngine.Object {
StartCoroutine(DoLoad(bundle, asset, OnLoad, OnLoadFailed));
}

private IEnumerator DoLoad(string bundlePath, string assetName, UnityAction<T> OnLoad, UnityAction OnLoadFailed);
T obj;

yield return null; //operational bundle loading goes here

if(obj != null) OnLoad.Invoke(obj); //we have an object
else OnLoadFailed.Invoke(); //something failed!
}
}

This next bit is going to replace those four lines inside DoLoad, as it chunk is going to be rather large, just wanted to make sure that the added layer to handle a coroutine was understood. All that's going on below is that we're requesting that an asset bundle be loaded, when that finishes, we have to request an asset out of the bundle, and when that finishes, call OnLoad.Invoke(obj). And at every step, if something goes pear-shaped, call OnLoadFailed.Invoke() instead.

string path = Path.Combine(Application.persistentDataPath,bundlePath);
if (File.Exists(path)) { //load asset from local computer (in Unity's persistent data directory)
AssetBundleCreateRequest bundleRequest = AssetBundle.LoadFromFileAsync(path);
yield return bundleRequest;

AssetBundle bundle= bundleRequest.assetBundle;
if (bundle == null) {
OnLoadFailed.Invoke();
return;
}
}
else {
OnLoadFailed.Invoke();
return;
}

AssetBundleRequest assetRequest = bundle.assetBundle.LoadAssetAsync<T>(assetName);
yield return assetRequest;

if (null != assetRequest.asset) {
onLoad?.Invoke((T)assetRequest.asset);
}
else {
onLoadFail?.Invoke();
}

Be sure to check out the documentation for additional details about fetching or using Asset Bundles in the code, including how to download from a remote location as well as the Application class for details on the persistentDataPath and related paths.

Problem with Asset Bundles in Unity 2020.3.30f1

i've found the solution.
The problem is of the unity "game" simulator, i've see this post on reddit and i've thought "wait, if i try the application on my phone, does it work correctly?".
The Answer is YES, on my Phone it works correctly and all the textures are charged with the asset.

Best way to dynamically load Assets in Unity WebGL Builds from external Source during Runtime

You can use Addressable Asset System with remote catalog for dynamic asset delivery using any CDN. Every time you run your game Addressable Asset System will download assets dynamically & asynchronously. This feature is successor of old asset bundle system.

Unity: How to build AssetBundles into separate directories per AssetBundle

I have solved this problem by writing a separate method to organise the assetbundle files once they have been built.

This works by taking the name of the assetbundle files (minus the extension) and creating a directory of the same name (if one does not already exist). Having created all directories, I then move the files of the same names into the relevant directory.

Note: On Windows (at least), you will have two files, the bundle itself and the manifest. The manifest has an extension but the bundle file does not. I am giving the file a temporary file extension so that I can create a directory of the same name, in the same directory that the extension-less bundle file resides, before reverting after moving.



Related Topics



Leave a reply



Submit