[Unity Study Notes] AssetBundle


What is an AB package?

AssetBundle is a compressed collection provided by Unity for storing resources. It can store any resource that Unity can recognize, such as models, textures, audio, scenes and other resources. Developer-defined binaries can also be loaded.

Why use AB package?

When resources are packaged, all resources will be packaged into Resources, which means that even if you only want some of the resources, you need to package all the resources and take them away; and because it is read-only, we cannot arbitrarily Modify the contents of the package.

In contrast, using AB packages can package different resources into independent packages. The storage path and compression method of AB packages can be customized, and the packaged content can also be dynamically updated later.

AB packages can save package resource size. On the one hand, the AB package allows better compression methods during compression, and the selected compression resources can also be selected at will. On the other hand, for example, in the game client, since the AB package is modifiable, the initial installation size can be reduced. When installing the game, we can set a smaller AB package for the game to run. When the player updates Then you can download the rest of the AB package content.

AB packages can also facilitate us to perform hot updates. For example, when playing King of Kings, we did not download the update, but the game interface was updated. This can be achieved by hot updating the downloaded AB package resources within the game. When the game needs to be updated and downloaded, the client usually first communicates with the server, obtains the address of the resource server, and then compares it with the AB package of the resource server to determine the resources that need to be updated.


How to export AB package

How to export AB package after installing AB package service?

1. Select the object and enter (select) the bound package name at the bottom of the Inspector interface.
2. View the packages that have been packaged in the AssetBundles panel
3. Select export options

Here are the export options

Attributes Options
Build Target Options correspond to the exported platform
Output Export path, AB package root path AssetBundles is at the same level as Asset
Clear Folders Clear export folder
Copy to StreamingAssets After the export is completed, copy the exported AB package to StreamingAssets in the Assets folder.

Some more important parameters

Attributes Options
Compression Compression mode: No Compression No compression, no need to unpack, the advantage is fast reading, but the file is large
LZMA standard compression, high compression rate, but troublesome to unpack
LZ4 block compression. When using resources in the package, only the corresponding chunk will be decompressed. The memory usage is low. It is highly recommended.
Exclude Type Information Does not contain resource type information
Force Rebuild Force refactoring of all contents in the package
Ignore Type Tree Changes Ignore type tree changes when performing incremental build checks
Append Hash AssetBundle name appended hash (hash addressing, file verification)
Strict Mode If any errors are reported during this time, the build fails
Dry Run Build Do a dry run build. This option allows you to do dry-run builds against AssetBundles without actually building them. When this option is enabled, BuildPipeline.BuildAssetBundles will still return an AssetBundleManifest object containing valid AssetBundle dependencies and hashes.

In addition, in unity, C# cannot be packaged into AB packages, so we need to learn Lua (haha, so funny). When packaged into an AB package, the C# script on the object actually only contains the mapping corresponding to FileID.

4. Open the corresponding resource in Inspect to check the information in the exported AB package.

In addition, the resources used by packaged objects, such as shaders, sprites, etc., will be automatically packaged (and by default in the same package body as the objects using them) even if they are not actively packaged. At this point we can select which package it belongs to in AssetLabel. However, it should be noted that if the resources that the object depends on are included in other packages, loading the object alone will often cause the loss of other dependent resources. Therefore, when loading this object, we also need to load its dependent packages at the same time.

AB package export file

Insert image description here

After packaging is completed, each package will generate a package and a corresponding manifest list. In addition, there is a package with the same name as the folder, called the main package , which stores information about the dependencies between packages. All packages have no suffix and are binary files. Manifest contains information from resources.

We often check StreamingAssets for the convenience of calling in code, usually using:

Application.streamingAssetsPath + "/" +"包名"

To read the path of the AB package


How to use AB package

Loading AB package

The loading of AB package is divided into two steps. The first step is to load the AB package first, and the second step is to load the corresponding resources in the package.

Synchronous loading

The simplest method is to load the AB package resources synchronously, and all codes are executed in sequence.

First load the AB package

AssetBundle AB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "包名");

Then load the resource. There are two ways to load resources, one is loading through generics, and the other is loading directly and specifying the type.

// T对应加载的资源的类型
T obj = AB.LoadAsset<T>("包内资源名");
// T对应加载的资源的类型
T obj = AB.LoadAsset("包内资源名", typeof(T)) as T;

From a practical point of view, the first loading method is good, but from a practical point of view, the second method is better because Lua does not support generics.


in this case

Asynchronous loading

Asynchronous loading is our recommended method, because Unity is single-threaded, and we do not want the main thread to be blocked due to the large number of resources during synchronous loading, which will cause the game to freeze. When it comes to asynchronous loading, the first thing that definitely comes to mind is coroutines. In addition to using coroutines to load, the AB package also provides some asynchronous loading methods.

    IEnumerator LoadABres(string ABName, string resName)
    {
    
    
        AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABName);
        yield return abcr;
        AssetBundleRequest abq = abcr.assetBundle.LoadAssetAsync(resName, typeof(Sprite));
        yield return abq;
        // 以加载Image组件的Sprite为例
        Sprite.sprite = abq.asset as Sprite;
    }

Uninstalling AB package

AssetBundle.UnloadAllAssetBundles(true);
AssetBundle.UnloadAllAssetBundles(false);

The UnloadAllAssetBundles method can uninstall all AB packages with one click, including two modes. In true mode, the AB packages in the scene will be uninstalled directly, which will cause the scene resources to be lost. Therefore, it is recommended to use false. When false, all AB packages will be uninstalled and the scene resources will not be lost.

Similarly, we can use the Unload method to uninstall the specified AB package, the same true and false.

AB.Unload(true);
AB.Unload(false);

Dependency loading

If a model depends on a resource, we can load both packages. But if a model depends on many resources, do we have to manually load so many packages? What's worse is what if you don't know which dependency package is?

Therefore we need the main package to obtain dependency information

// 加载主包
AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "主包包名");
// 获取主包清单
AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
// 从清单中获取依赖信息并保存在一个字符串数组中
string[] strs = abManifest.GetAllDependencies("需要加载的包名");
// for循环或者foreach都可以循环加载依赖包
foreach (string str in strs)
{
    
    
	AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + str);
}

There is a problem, now we do load the resource using the package body. But if in another package, its dependency package is the same as this package's dependency package, then if we load another package, an error will be reported! Because the same dependency package is loaded twice.

In order to avoid this problem, we need a manager to uniformly manage the loading and unloading of resource packages!


AB package explorer

First, let’s think about what characteristics a resource manager should have:

  • Globally unique, otherwise calls from other classes will still cause repeated loading (single case mode)
  • Dictionary storage, lists a package body usage list, and key-value pairs can avoid repeated writing
  • Asynchronous loading to ensure main thread safety

Then a singleton resource manager would be the best choice.

The main process is divided into four steps:

  • Write a function to read the main package to obtain dependencies
  • Define a dictionary to store the mapping between package names and AB package resources in the form of key-value pairs. The dictionary can avoid reading the same package repeatedly.
  • Rewrite Unity's method of reading the AB package so that it first reads the main package to obtain the dependencies of the read AB package, and then reads the AB package and all dependent packages.
  • Define the uninstall method and clear the dictionary
  • (and other custom extensible methods)

Here is just a small snippet of code:

Define a singleton base class of DontDestroyOnLoad that accepts generics

/// <summary>
/// 该单例继承于Mono,并且不会随着场景的切换而销毁
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class DDOLSingletonMono<T> : MonoBehaviour where T : MonoBehaviour
{
    
    
    private static T _instance;
    public static T Instance
    {
    
    
        get
        {
    
    
            if (_instance == null)
            {
    
    
                _instance = FindObjectOfType<T>();
                if (_instance == null)
                {
    
    
                    GameObject obj = new GameObject();
                    obj.name = typeof(T).Name;
                    DontDestroyOnLoad(obj);
                    _instance = obj.AddComponent<T>();
                }
            }
            return _instance;
        }
    }
}

Define an ABManager, which inherits the above base class and defines management methods within it

    /// <summary>
    /// 加载AB包
    /// </summary>
    /// <param name="abName"></param>
    public void LoadAB(string abName)
    {
    
    
        // 获取主包
        if (mainAB == null)
        {
    
    
            mainAB = AssetBundle.LoadFromFile(PathUrl + MainABName);
            manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }
        AssetBundle ab = null;
        // 获取所用资源包依赖信息
        string[] relys = manifest.GetAllDependencies(abName);
        foreach (string rely in relys)
        {
    
    
            // 判断依赖包是否已加载
            if (!abDic.ContainsKey(rely))
            {
    
    
                ab = AssetBundle.LoadFromFile(PathUrl + rely);
                abDic.Add(rely, ab);
            }
        }
        // 加载资源包
        if (!abDic.ContainsKey(abName))
        {
    
    
            ab = AssetBundle.LoadFromFile(PathUrl + abName);
            abDic.Add(abName, ab);
        }
    }
    /// <summary>
    /// 异步加载资源,GameObject类型可直接帮助实例化
    /// </summary>
    /// <param name="abName">加载的资源包</param>
    /// <param name="resName">加载的资源名称</param>
    /// <param name="callBack"></param>
    public void LoadResAsync(string abName, string resName, UnityAction<Object> callBack)
    {
    
    
        StartCoroutine(LoadResAsyncCoroutine(abName, resName, callBack));
    }
    private IEnumerator LoadResAsyncCoroutine(string abName, string resName, UnityAction<Object> callBack)
    {
    
    
        LoadAB(abName);
        // 如果是GameObject,帮助实例化
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName);
        yield return abr;
        if (abr.asset == null)
        {
    
    
            Debug.LogError("异步加载失败!读取资源为空!");
        }
        if (abr.asset is GameObject)
            callBack(Instantiate(abr.asset));
        else
            callBack(abr.asset);
    }

The other codes are similar, and they are nothing more than rewriting the official three synchronous loading and asynchronous loading methods to read the dependency package first, and by the way help the GameObject to be instantiated.
The specific code can be viewed in [Tang Laoshi] Unity Hot Update AssetBundle

Using AB Package Manager can avoid confusing package relationships in the project. It is recommended with five stars.

Guess you like

Origin blog.csdn.net/milu_ELK/article/details/131815540