Unity AssetBundle(1):Assets packaging and dependencies (Dependencies)

I have a little understanding of the AssetBundle dependency mechanism after Unity5.x, and created a project for verification github:GeWenL / AssetBundlePro AbScene.unity

What are the resources? Some common types of Asset

The assetbundle packaging command is BuildPipeline.BuildAssetBundles, and the format is:

Quote:

Improper handling of dependencies is an important cause of resource redundancy.

My test project uses uncompressed (UncompressedAssetBundle) method;

4 Sprite textures (uncompressed), tex1-4; individually packaged into uncompressed ab packages, the size is as shown in the figure below.

There are now 5 prefabs, sprite1.prefab is associated with texture tex1, sprite2.prefab is associated with texture tex2, sprite3.prefab is associated with texture tex3, sprite12.prefab is associated with texture tex1/tex2, sprite123.prefab is associated with texture tex1/tex2/tex3.

Pack these 5 prefabs into ab packages respectively, prefabsp1, prefabsp2, prefabsp3, prefabsp12, and prefabsp123.

Divided into two situations:

These 5 prefab ab packages are very small in size and are only associated with the texture ab package.

For example, the manifest files of the two prefab ab packages of sprite1 and sprite123 show that the associated tex1.png, tex2.png, and tex3.png have been included in the corresponding ab package, so they must rely on the corresponding ab package. Dependencies are dependencies. ab list.

At this time, only sprite123.prefab(AssetBundle.LoadFromFile + bundle.LoadAsset + Instantiate) is loaded:

You will find that the images on the Image objects using tex1.png, tex2.png, and tex3.png have been missed and are displayed in the wrong white color. The reason is that prefabsp123 does not contain these three pictures (verified by UnityStudio). It only loads it and there is no way to find the corresponding texture.

If you want to display it normally, you need to load the ab packages corresponding to tex1.png, tex2.png, and tex3.png in advance. For example, load the ab package of tex1 and tex2 in advance. The effect is as follows: (tex1 and tex2 are displayed normally, but tex3 is not loaded and not normal. show)

Using UnityStudio to view the ab package, we can see that prefabsp1 not only contains sprite1.prefab, but also writes (written out to this single file) Sprite-tex1.png. Similarly, prefabsp12 and prefabsp123 also have their own copy of tex1.png, as shown below shown.

In this packaging method, resources that are referenced multiple times will have a separate copy in each AssetBundle that uses it. This is the often mentioned resource redundancy.

The core is: avoid resource redundancy
The approach is:

AssetBundle in Unity

Concept of AssetBundle

AssetBundle, also known as AB package, is a resource compression package provided by Unity for storing resources. It is an extension of Unity’s initial Resources; the generally used strategy is to put necessary resources and resources that do not need to be updated in the Resources file. Clip it and place other resources under AssetBundle.

The AssetBundle system in Unity is an extension of resource management. By distributing resources in different AB packages, it can minimize the memory pressure at runtime. It can dynamically load and unload AB packages, and then selectively load content. .

Advantages of AssetBundle

  1. The AB package storage location is customized and can then be placed in a readable and writable path to facilitate hot updates.
  2. AB packet custom compression method, you can choose no compression or choose compression methods such as LZMA and LZ4 to reduce the size of the packet and enable faster network transmission.
  3. Resources can be distributed in different AB packages to minimize the memory pressure during runtime, and can be loaded immediately and selectively load the required content.
  4. AB packages support dynamic updates at a later stage, significantly reducing the size of the initial installation package. Non-core resources are uploaded to the server in the form of AB packages and dynamically loaded during later runs to improve user experience.

Comparison of AssetBundle and Resources

AssetBundle Resources
Resources can be distributed in multiple packages All resources are packaged into one large package
The storage location is flexible and customizable Must be stored in the Resources directory
Flexible compression method (LZMA, LZ4) All resources will be compressed into binary
Support dynamic updates later After packaging, resources are read-only and cannot be changed dynamically

Characteristics of AssetBundle

  1. The AB package can store most Unity resources but cannot directly store C# scripts, so hot updates of code need to use Lua or store compiled DLL files.
  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. Direct loading may cause some resources to be lost. That is, there are dependencies between AB packages. When loading the current AB packages need to load the packages they depend on together.
  4. After packaging is completed, a main package will be automatically generated (the name of the main package varies with different platforms). The version number, check code (CRC), and all other package-related information (names, dependencies) will be stored in the manifest of the main package. )

AssetBundle full packaging process

This article mainly introduces the AB package management plug-in AssetBundle Browser officially provided by Unity for packaging.

  1. To obtain the AssetBundleBrowser plug-in, the Unity 2019 version can find this plug-in directly in Windows ->PackageManager and install it directly

  2. After version 2020 or other versions, you may not be able to find this plug-in in method 1. You can search and download the compressed package on github, and decompress the downloaded installation package to the Packages folder of the Unity project (be sure to decompress)

Using the AssetBundleBrowser panel

After correctly obtaining and installing the plug-in, open the AB package management panel through Windows —-> AssetBundle Browser. There are three panels in total.

Configure Panel: Can view the basic situation of the current AB package and its internal resources (size, resources, dependencies, etc.)

Build Panel: Responsible for the settings related to AssetBundle packaging. Press Build to package.

Three compression methods

NoCompression: No compression, fast decompression, large package size, not recommended.
LZMA: Minimum compression, slow decompression. To use one resource, you need to decompress all resources in the package.
LZ4: slightly larger compression, faster decompression, use whatever you use to decompress, and low memory usage. This is generally recommended.
Generally, the settings that need to be changed are the relevant option settings checked in the picture.

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

3. Set the AssetBundle package to which the resource belongs

At the bottom of the Inspector panel of the resource that needs to be packaged, you can select which AB package it should be placed under. You can also create a new AB package to put the resource into it. After placing it, Build the package again to put the resource into the corresponding AB package. middle.

Code examples for loading and unloading AssetBundle

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ABLoadTest : MonoBehaviour

    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
                                                     

    private string basePath
    
        get
        
#if UNITY_EDITOR || UNITY_STANDALONE
            return Application.dataPath + "/StreamingAssets/";
#elif UNITY_IPHONE
                return Application.dataPath + "/Raw/";
#elif UNITY_ANDROID
                return Application.dataPath + "!/assets/";
#endif
        
    

    void Start()
    
        abCache = new Dictionary<string, AssetBundle>();

        var ab = LoadABTest();
        GameObject model = ab.LoadAsset<GameObject>("Cube");
        var b = Instantiate<GameObject>(model);
        //dosomething


    

    AssetBundle LoadABTest()
    
        AssetBundle ab;
        string abName = "3dmodel.first";
        if (mainAB == null)
        
            mainAB = AssetBundle.LoadFromFile(basePath + "StandaloneWindows");
            mainManifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

        
        //Get the names of all dependent packages based on manifest Fixed API
        string[] dependencies = mainManifest.GetAllDependencies(abName);
        //Loop to load all dependent packages
        for (int i = 0; i < dependencies.Length; i + + )
        
            //If not in cache, add it
            if (!abCache.ContainsKey(dependencies[i]))
            
                //Load based on dependency 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 -- pay attention to caching issues in the same way
        if (abCache.ContainsKey(abName))
        
            Debug.Log($"have load abName");
            return abCache[abName];
        
        else
        
            ab = AssetBundle.LoadFromFile(basePath + abName);
            abCache.Add(abName, ab);
            Debug.Log($"new load abName");
            return ab;
        
    

    // Update is called once per frame
    void Update()
    
        if(Input.GetKeyDown(KeyCode.A)) // Synchronous loading
        
            var ab = LoadABTest();
            GameObject model = ab.LoadAsset<GameObject>("Cube");
            var b = Instantiate<GameObject>(model);
        
        else if (Input.GetKeyDown(KeyCode.S))// Asynchronous loading
        
            var ab = LoadABTest();
            StartCoroutine(LoadResAsyncTest(ab));
            
        
        else if (Input.GetKeyDown(KeyCode.D))//Single uninstall
        
            UnLoad("3dmodel.first");
            Debug.Log("have UnLoadAll 3dmodel.first");

        
        else if (Input.GetKeyDown(KeyCode.F))//Uninstall all
        
            UnLoadAll();
            Debug.Log("have UnLoadAll");
        

    

    private IEnumerator LoadResAsyncTest(AssetBundle ab)
    
        if (ab == null) yield return null;
        var model1 = ab.LoadAssetAsync<GameObject>("Cube");
        yield return model1;
        var async_model = Instantiate((GameObject)model1.asset);
        //dosomething
    

    //====================Two ways to uninstall AB package==================
    //Uninstall a single package
    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);
        //Pay attention to clear the cache
        abCache.Clear();
        mainAB = null;
        mainManifest = null;