[AssetBundle] Things about AssetBundle management that may be useful to you.

Hi everyone:

I was always wondering about how to unload Asset bundles properly. The key is how to keep an track of all the references in the scene, and how to de-reference them properly.

Before I share my solution, I wanted to give some test results:

AssetBundle bundle = AssetBundle.LoadFromFile("Assets/SomeAssetBundle.unity3d");
for (int i = 0; i < 10000; i++)
{
    // could be other type of assets
    Sprite sp = bundle.LoadAsset<Sprite>("a.png");
    Debug.Log(sp.GetInstanceID());
}

Loading from assetbundle wich the same asset path will always return the same instance, unless you unload the assetbundle and reload.

Why did I do that test? Because I’ve been confused for a long time: which should I keep a reference on, assets? or asset bundles?. I mean, if I keep a track of loaded assets, instead of asset bundles, the next time when I try to load an asset, I could just return the loaded asset. So every time when an asset load request is on, we could just load the asset from assetbundle, and store the asset, and unload the assetbundle right away.

But that is not the correct answer that I’m looking for. As I tested out, load and unload asset bundles could be expensive. So asset bundles are the one that should be referenced.

To reference an asset bundle is easy, but how to de-reference them so that we know when to unload them?(when reference count <= 0)

Here is the code that I just wrote, and it works fine for me. Though I haven’t put the async-load methods in it.

        public static T GetOrCreateComponent<T>(this GameObject go) where T : UnityEngine.Component
        {
            T com = go.GetComponent<T>();
            if (com == null)
            {
                com = go.AddComponent<T>();
            }

            return com;
        }
       
        public static void BindAsset(this GameObject go, Object asset)
        {
            go.GetOrCreateComponent<AssetRecorder>().BindAsset(asset);
        }

        ///////////////////////////////////////////////////////////
        // All type of assets
        public static void LoadImage(this UnityEngine.UI.Image img, string assetPath)
        {
            // Unload first
            int oldInstanceID = -1;
            if (img.sprite != null) oldInstanceID = img.sprite.GetInstanceID();

            img.sprite = AssetLoader.LoadAsset<Sprite>(assetPath);
            if (oldInstanceID >= 0 && img.GetInstanceID() != oldInstanceID)
            {
                AssetLoader.RemoveRef(oldInstanceID);
            }

            img.gameObject.BindAsset(img.sprite);
        }

So the idea is to use a Mono script that records all the assets the gameObject has “asked” from an asset bundle. By doing that, we now can do the dereference in the OnDestroy callback:

        protected void OnDestroy()
        {
            for (int i = 0; i < assetBundleRefs.Count; i++)
            {
                // do the unbind operation
                AssetLoader.RemoveRef(assetBundleRefs[i]);
            }
        }

That’s the key idea. But there is one problem though: you must destroy the gameObject, instead of components.

So this assetbundle framework is not completely finished. I’d like to share when it’s done. And I really appreciate if anyone would share their thoughts on ab management, cause I struggled a lot all along the way.

This is all reasonable, but I’m not sure I would bother binding assets like that. Who cares? Unity takes care of that for you. You should not really care where an asset originally came from: scene reference, prefab, Resources.Load(), AssetBundle load, etc. Let Unity do its usual bookkeeping.

Loading and keeping references to AssetBundle contents is no different from any other asset in UnityeD: you may keep references to those assets yourself, with all the caveats that entails: they normally go away when you change scenes, they take up memory when the reference is held, if you drop the reference you can call Resources.UnloadAllUnusedAssets(), etc.

The key is that you probably only want to Load “a few” of your asset bundles at once, and you must have a centralized area this happens: if you Load an asset bundle and another part of your code tries to Load it, it will fail unless you first unload the first one, and as you pointed out, that may take noticeable resources. They’re sort of like file handles that way.

Thanks for your reply. Indeed it shouldn’t have been such a deal. I got myself in this mess when I first looked into the unity’s documentations on Assetbundle and resources(I only used Resources.Load before, and also never cared about any resource memory issues before). The document specifically points out what happens when Assetbundle.Unload(false), and that made me thinking : “Why would I unload before I clear its references? Oh, that’s because I couldn’t know which objects are referencing it.”… So that’s basically why I started to think how to do the references…

I’ve watched the Unity official videos about their future plans on Assetbundle, that was back in 2016s. And they did say internal reference management, which I think developers should not bother so much on this any more. :smile: