unity-game-engineinstantiationassetbundle

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


I have some assetbundles which we create for unity 5.6 automatically, any of these assetbudles has one game object inside it.

my example asset: c6f97739ec43264ef3bfae7b1dc55e88.unity3d

link: https://www.file-up.org/891dtdi54n0l

so, we have already used assetBundle.mainAsset to access to that one game object. but in unity 2018.3 , the assetBundle.mainAsset is obsolete.

i read the unity document and they say use

LoadAsset<GameObject>(name);

with name or type ! but when i create asset bundle, i don't know the gameobject name to access it.

i try to load it with : myLoadedAssetBundle.GetAllAssetNames()[0]).

void LoadAssetBundle(string bundleUrl)
{
    myLoadedAssetBundle = AssetBundle.LoadFromFile(bundleUrl);
    Debug.Log(myLoadedAssetBundle == null ? "Faild To Load" : "Succesfully Loaded!");

    Debug.Log(myLoadedAssetBundle.LoadAsset(myLoadedAssetBundle.GetAllAssetNames()[0]));

    GameObject prefab = myLoadedAssetBundle.LoadAsset<GameObject>(myLoadedAssetBundle.GetAllAssetNames()[0]);

    Instantiate(prefab);

    myLoadedAssetBundle.Unload(true);
}

my question is :

  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.

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


Solution

  • 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.