Unity AssetBundleBrowser plugin

AssetBundle Features

1. The AB package can store most of the Unity resources but cannot directly store C# scripts, so the hot update of the code needs to use Lua or store the compiled DLL file, as well as the HybridCLR hot update plug-in, and the code resources can be hot updated.

2. The AB package cannot be loaded repeatedly. When the AB package has been loaded into the memory, it must be unloaded before it can be reloaded.

3. Multiple resources are distributed in different AB packages. Some resources such as the texture of a prefab may not be in the same package. Some resources will be lost when directly loaded, that is, there are dependencies between AB packages. When loading the current AB package, you need to load the packages it depends on.

4. After the packaging is completed, a main package will be automatically generated (the name of the main package varies with the platform), and the version number, check code (CRC), and all other package related information (name, dependencies)

Plugin Overview

Download address: https://github.com/Unity-Technologies/AssetBundles-Browser

1. Configure panel: You can view the basic situation of the current AB package and its internal resources (size, resources, dependencies, etc.), and also configure those ab packages packaged during Build

2. Build panel: Responsible for the related settings of AssetBundle packaging, press Build to start packaging

2.1 Three compression methods

1. NoCompression: No compression, fast decompression, large package, not recommended.
2. LZMA: Minimal compression, slow decompression, use one resource to decompress all resources.
3. LZ4: The compression is slightly larger, and the decompression is fast. What to use to decompress, the memory usage is low, and it is recommended to use it.
Generally, the settings that need to be changed are the relevant option settings ticked in the figure.

3. Inspect panel: It is mainly used to view some details of the packaged AB package file (size, resource path, etc.)

Load related API

//AB package loading required related API
//1. Load the AB package according to the path Note that the AB package cannot be loaded repeatedly
AssetBundle ab = AssetBundle. LoadFromFile(path);
//2. Load the resource with the specified name and type under the ab package
T obj = ab.LoadAsset<T>(ResourceName); //generic loading
Object obj = ab.LoadAsset(ResourceName); //Non-generic loading, the type needs to be forced for subsequent use
Object obj = ab.LoadAsset(ResourceName,Type); //The parameter specifies the resource type to prevent duplicate names
T obj = ab.LoadAssetAsync<T>(resName); //Asynchronous generic loading
Object obj = ab.LoadAssetAsync(resName); //Asynchronous non-generic loading
Object obj = ab.LoadAssetAsync(resName,Type); //The parameter specifies the resource type to prevent duplicate names
//3. Uninstall the ab package The bool parameter represents whether to delete the resources that have been loaded into the scene from this AB package (usually false)
ab.UnLoad(false); //Unload a single ab package
AssetBundle.UnloadAllAssetBundles(false); //Unload all AB packages

Sample code:

using System;
using System.Net.Mime;
using System. Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Common
{
    /// <summary>
    /// AB package manager is globally unique and uses singleton mode
    /// </summary>
    public class ABManager : MonoSingleton<ABManager>
    {
        //AB package cache --- solve the problem that the AB package cannot be loaded repeatedly, and it is also conducive to improving efficiency.
        private Dictionary<string, AssetBundle> abCache;

        private AssetBundle mainAB = null; //main package

        private AssetBundleManifest mainManifest = null; //Configuration file in the main package --- used to obtain dependent packages

        //Basic paths under each platform --- Use macros to determine the streamingAssets path under the current platform
        private string basePath { get
            {
            //Use the StreamingAssets path and pay attention to check copy to streamingAssets when packaging the AB package
#if UNITY_EDITOR || UNITY_STANDALONE
                return Application.dataPath + "/StreamingAssets/";
#elif UNITY_IPHONE
                return Application.dataPath + "/Raw/";
#elif UNITY_ANDROID
                return Application.dataPath + "!/assets/";
#endif
            }
        }
        //The name of the main package under each platform --- used to load the main package to obtain dependency information
        private string mainABName
        {
            get
            {
#if UNITY_EDITOR || UNITY_STANDALONE
                return "Standalone Windows";
#elif UNITY_IPHONE
                return "IOS";
#elif UNITY_ANDROID
                return "Android";
#endif
            }
        }

        //Inherit the initialization function provided by the singleton mode
        protected override void Init()
        {
            base.Init();
            //Initialize the dictionary
            abCache = new Dictionary<string, AssetBundle>();
        }


        //Load the AB package
        private AssetBundle LoadABPackage(string abName)
        {
            AssetBundle ab;
            //Load the ab package, and its dependent packages need to be loaded together.
            if (mainAB == null)
            {
                //Load the main package according to the basic path and main package name under each platform
                mainAB = AssetBundle.LoadFromFile(basePath + mainABName);
                //Get the AssetBundleManifest resource file under the main package (with dependency information)
                mainManifest = mainAB. LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            }
            // Get the names of all dependent packages according to the manifest Fixed API
            string[] dependencies = mainManifest. GetAllDependencies(abName);
            //Cycle load all dependent packages
            for (int i = 0; i < dependencies. Length; i ++ )
            {
                // join if not in cache
                if (!abCache. ContainsKey(dependencies[i]))
                {
                    //Load according to the dependent package name
                    ab = AssetBundle.LoadFromFile(basePath + dependencies[i]);
                    //Pay attention to adding it to the cache to prevent repeated loading of AB packages
                    abCache. Add(dependencies[i], ab);
                }
            }
            //Load the target package -- also pay attention to the cache problem
            if (abCache.ContainsKey(abName)) return abCache[abName];
            else
            {
                ab = AssetBundle.LoadFromFile(basePath + abName);
                abCache. Add(abName, ab);
                return ab;
            }


        }
        
        
        //==================== Three resource synchronous loading methods ===================
        //Provide a variety of calling methods to facilitate calling in other languages (Lua does not support generics well)
        Three overloads for synchronous loading of #region
        
        /// <summary>
        /// Synchronously load resources---generic loading is simple and intuitive, no display conversion required
        /// </summary>
        /// <param name="abName">The name of the ab package</param>
        /// <param name="resName">Resource name</param>
        public T LoadResource<T>(string abName, string resName) where T:Object
        {
            //load the target package
            AssetBundle ab = LoadABPackage(abName);

            //return resource
            return ab.LoadAsset<T>(resName);
        }

        
        //Do not specify the type. It is not recommended to use it if there are duplicate names. When using it, the conversion type needs to be displayed
        public Object LoadResource(string abName, string resName)
        {
            //load the target package
            AssetBundle ab = LoadABPackage(abName);

            //return resource
            return ab.LoadAsset(resName);
        }
        
        
        //Using parameters to pass types, suitable for language calls that do not support generics, need to force the type when using
        public Object LoadResource(string abName, string resName, System.Type type)
        {
            //load the target package
            AssetBundle ab = LoadABPackage(abName);

            //return resource
            return ab.LoadAsset(resName,type);
        }

        #endregion
        
        
        //================== Three resource asynchronous loading methods ========================

        /// <summary>
        /// Provide asynchronous loading ---- Note that the loading of the AB package here is synchronous loading, but the loading of resources is asynchronous
        /// </summary>
        /// <param name="abName">ab package name</param>
        /// <param name="resName">Resource name</param>
        public void LoadResourceAsync(string abName, string resName, System.Action<Object> finishLoadObjectHandler)
        {
            AssetBundle ab = LoadABPackage(abName);
            //Enable the coroutine to provide a delegate after the resource is loaded successfully
            StartCoroutine(LoadRes(ab, resName, finishLoadObjectHandler));
        }

        
        private IEnumerator LoadRes(AssetBundle ab, string resName, System.Action<Object> finishLoadObjectHandler)
        {
            if (ab == null) yield break;
            //Asynchronous loading resource API
            AssetBundleRequest abr = ab.LoadAssetAsync(resName);
            yield return abr;
            // delegate call processing logic
            finishLoadObjectHandler(abr.asset);
        }
        
        
        //Asynchronously load resources according to Type
        public void LoadResourceAsync(string abName, string resName, System.Type type, System.Action<Object> finishLoadObjectHandler)
        {
            AssetBundle ab = LoadABPackage(abName);
            StartCoroutine(LoadRes(ab, resName,type, finishLoadObjectHandler));
        }
        
       \t
        private IEnumerator LoadRes(AssetBundle ab, string resName, System.Type type, System.Action<Object> finishLoadObjectHandler)
        {
            if (ab == null) yield break;
            AssetBundleRequest abr = ab.LoadAssetAsync(resName,type);
            yield return abr;
            // delegate call processing logic
            finishLoadObjectHandler(abr.asset);
        }

        
        // Generic loading
        public void LoadResourceAsync<T>(string abName, string resName, System.Action<Object> finishLoadObjectHandler)where T:Object
        {
            AssetBundle ab = LoadABPackage(abName);
            StartCoroutine(LoadRes<T>(ab, resName, finishLoadObjectHandler));
        }

        private IEnumerator LoadRes<T>(AssetBundle ab, string resName, System.Action<Object> finishLoadObjectHandler)where T:Object
        {
            if (ab == null) yield break;
            AssetBundleRequest abr = ab.LoadAssetAsync<T>(resName);
            yield return abr;
            // delegate call processing logic
            finishLoadObjectHandler(abr.asset as T);
        }


        //======================= Two ways to uninstall the AB package ==================
        //Single package uninstall
        public void UnLoad(string abName)
        {
            if(abCache. ContainsKey(abName))
            {
                abCache[abName].Unload(false);
                //Note that the cache needs to be removed together
                abCache. Remove(abName);
            }
        }

        // uninstall all packages
        public void UnLoadAll()
        {
            AssetBundle. UnloadAllAssetBundles(false);
            //Note to clear the cache
            abCache. Clear();
            mainAB = null;
            mainManifest = null;
        }
    }
}