Explicación detallada del paquete AB en Unity

0. Enlace de referencia

Enlace del artículo del autor original: https://blog.csdn.net/Q540670228/article/details/122795671

1. Concepto de paquete de activos

AssetBundle, también conocido como paquete AB, es un paquete de compresión de recursos proporcionado por Unity para almacenar recursos.

El sistema AssetBundle en Unity es una extensión de la gestión de recursos. Al distribuir recursos en diferentes paquetes AB, puede minimizar la presión de la memoria en tiempo de ejecución. Puede cargar y descargar paquetes AB dinámicamente y luego cargar contenido de forma selectiva.

Ventajas de AssetBundle:

  1. La ubicación de almacenamiento del paquete AB se personaliza y luego se puede colocar en una ruta de lectura y escritura para facilitar las actualizaciones en caliente.
  2. Método de compresión personalizado de paquetes AB, puede elegir no comprimir o elegir métodos de compresión como LZMA y LZ4 para reducir el tamaño del paquete y permitir una transmisión de red más rápida.
  3. Los recursos se pueden distribuir en diferentes paquetes AB para minimizar la presión de la memoria durante el tiempo de ejecución, y se pueden cargar inmediatamente y cargar selectivamente el contenido requerido.
  4. Los paquetes AB admiten actualizaciones dinámicas en una etapa posterior, lo que reduce significativamente el tamaño del paquete de instalación inicial. Los recursos no centrales se cargan en el servidor en forma de paquetes AB y se cargan dinámicamente durante ejecuciones posteriores para mejorar la experiencia del usuario.

2. Comparación entre AssetBundle y Recursos

Paquete de activos Recursos
Los recursos se pueden distribuir en varios paquetes. Todos los recursos están empaquetados en un gran paquete.
Personalización flexible de la ubicación de almacenamiento Debe almacenarse en el directorio de Recursos.
Métodos de compresión flexibles (LZMA, LZ4) Todos los recursos se comprimirán en binario.
Admite actualizaciones dinámicas más tarde Después del empaquetado, los recursos son de solo lectura y no se pueden cambiar dinámicamente.

3. Características del paquete de activos

  • El paquete AB puede almacenar la mayoría de los recursos de Unity, pero no puede almacenar directamente scripts de C#, por lo que las actualizaciones en caliente del código deben utilizar Lua o almacenar archivos DLL compilados.
  • El paquete AB no se puede cargar repetidamente. Cuando el paquete AB se ha cargado en la memoria, debe descargarse antes de poder recargarse.
  • Múltiples recursos se distribuyen en diferentes paquetes AB. Algunos recursos, como la textura de una casa prefabricada, pueden no estar en el mismo paquete. La carga directa puede causar que se pierdan algunos recursos. Es decir, existen dependencias entre los paquetes AB. Al cargar el actual Los paquetes AB necesitan cargar juntos los paquetes de los que dependen.
  • Una vez completado el empaquetado, se generará automáticamente un paquete principal (el nombre del paquete principal varía según las diferentes plataformas). Se almacenará el número de versión, el código de verificación (CRC) y toda la demás información relacionada con el paquete (nombres, dependencias). en el manifiesto del paquete principal).

Insertar descripción de la imagen aquí

4. Proceso de empaquetado completo de AssetBundle

4.1 Obtener el complemento AssetBundleBrowser

Método 1: la versión Unity 2019 puede encontrar este complemento directamente en Windows -> PackageManager e instalarlo directamente
Insertar descripción de la imagen aquí

Después de la versión 2020 u otras versiones, es posible que no pueda encontrar este complemento en el método 1. Puede buscar y descargar el paquete comprimido en github. La dirección de descarga: https://github.com/Unity-Technologies/AssetBundles -El navegador
Insertar descripción de la imagen aquí
descargará el paquete de instalación. Descomprímalo en la carpeta Paquetes del proyecto Unity (asegúrese de descomprimirlo)
Insertar descripción de la imagen aquí

4.2 Uso del panel AssetBundleBrowser

  • Panel de configuración: puede ver la situación básica del paquete AB actual y sus recursos internos (tamaño, recursos, dependencias, etc.)
    Insertar descripción de la imagen aquí
  • Panel de compilación: responsable de la configuración relevante del paquete AssetBundle. Presione Construir para empaquetar.
    Insertar descripción de la imagen aquí
    Tres métodos de compresión.
  1. NoCompression: Sin compresión, descompresión rápida, tamaño de paquete grande, no recomendado.
  2. LZMA: tiene la compresión más pequeña y la descompresión lenta. Para utilizar un recurso, debe descomprimir todos los recursos del paquete.
  3. LZ4: La compresión es un poco mayor, la descompresión es más rápida, puedes usar lo que uses para descomprimir y el uso de memoria es bajo, es más recomendado.
    Generalmente, las configuraciones que deben cambiarse son las configuraciones de opciones relevantes marcadas en la imagen.
  • Panel de inspección: se utiliza principalmente para ver algunos detalles del archivo del paquete AB empaquetado (tamaño, ruta de recursos, etc.)
    Insertar descripción de la imagen aquí

4.3 Establecer el paquete AssetBundle al que pertenece el recurso

Insertar descripción de la imagen aquí
En la parte inferior del panel Inspector del recurso que necesita ser empaquetado, puede seleccionar en qué paquete AB se debe colocar. También puede crear un nuevo paquete AB para colocar el recurso en él. Después de colocarlo, construya el paquete. nuevamente para colocar el recurso en el paquete AB correspondiente.

5. Administrador de paquetes de activos

El principal requisito del administrador de paquetes AB: cargar los recursos especificados en el paquete AB especificado. Los pasos básicos son los siguientes:

  1. Cargue el paquete AB donde se encuentra el recurso y todos sus paquetes dependientes (busque el nombre del paquete dependiente de acuerdo con la información del manifiesto del paquete principal)
  2. Cargue el recurso especificado del paquete AB (según nombre, tipo)
  3. Descargue el paquete AB cargado cuando ya no esté en uso.
    Principales API relacionadas
//AB包加载所需相关API
//1. 根据路径进行加载AB包 注意AB包不能重复加载
AssetBundle ab = AssetBundle.LoadFromFile(path);
//2. 加载ab包下指定名称和类型的资源
T obj = ab.LoadAsset<T>(ResourceName); //泛型加载
Object obj = ab.LoadAsset(ResourceName); //非泛型加载 后续使用时需强转类型
Object obj = ab.LoadAsset(ResourceName,Type); //参数指明资源类型 防止重名
T obj = ab.LoadAssetAsync<T>(resName); //异步泛型加载
Object obj = ab.LoadAssetAsync(resName); //异步非泛型加载
Object obj = ab.LoadAssetAsync(resName,Type); //参数指明资源类型 防止重名
//3. 卸载ab包 bool参数代表是否一并删除已经从此AB包中加载进场景的资源(一般为false)
ab.UnLoad(false); //卸载单个ab包
AssetBundle.UnloadAllAssetBundles(false); //卸载所有AB包

El siguiente es el código detallado y los comentarios del administrador de paquetes AB. El contenido del modo singleton involucrado se presentará brevemente más adelante.

ABManager.cs

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

namespace Common
{
    
    
    /// <summary>
    /// AB包管理器 全局唯一 使用单例模式
    /// </summary>
    public class ABManager : MonoSingleton<ABManager>
    {
    
    
        //AB包缓存---解决AB包无法重复加载的问题 也有利于提高效率。
        private Dictionary<string, AssetBundle> abCache;

        private AssetBundle mainAB = null; //主包

        private AssetBundleManifest mainManifest = null; //主包中配置文件---用以获取依赖包

        //各个平台下的基础路径 --- 利用宏判断当前平台下的streamingAssets路径
        private string basePath {
    
     get
            {
    
    
            //使用StreamingAssets路径注意AB包打包时 勾选copy to streamingAssets
#if UNITY_EDITOR || UNITY_STANDALONE
                return Application.dataPath + "/StreamingAssets/";
#elif UNITY_IPHONE
                return Application.dataPath + "/Raw/";
#elif UNITY_ANDROID
                return Application.dataPath + "!/assets/";
#endif
            }
        }
        //各个平台下的主包名称 --- 用以加载主包获取依赖信息
        private string mainABName
        {
    
    
            get
            {
    
    
#if UNITY_EDITOR || UNITY_STANDALONE
                return "StandaloneWindows";
#elif UNITY_IPHONE
                return "IOS";
#elif UNITY_ANDROID
                return "Android";
#endif
            }
        }

        //继承了单例模式提供的初始化函数
        protected override void Init()
        {
    
    
            base.Init();
            //初始化字典
            abCache = new Dictionary<string, AssetBundle>();
        }


        //加载AB包
        private AssetBundle LoadABPackage(string abName)
        {
    
    
            AssetBundle ab;
            //加载ab包,需一并加载其依赖包。
            if (mainAB == null)
            {
    
    
                //根据各个平台下的基础路径和主包名加载主包
                mainAB = AssetBundle.LoadFromFile(basePath + mainABName);
                //获取主包下的AssetBundleManifest资源文件(存有依赖信息)
                mainManifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            }
            //根据manifest获取所有依赖包的名称 固定API
            string[] dependencies = mainManifest.GetAllDependencies(abName);
            //循环加载所有依赖包
            for (int i = 0; i < dependencies.Length; i++)
            {
    
    
                //如果不在缓存则加入
                if (!abCache.ContainsKey(dependencies[i]))
                {
    
    
                    //根据依赖包名称进行加载
                    ab = AssetBundle.LoadFromFile(basePath + dependencies[i]);
                    //注意添加进缓存 防止重复加载AB包
                    abCache.Add(dependencies[i], ab);
                }
            }
            //加载目标包 -- 同理注意缓存问题
            if (abCache.ContainsKey(abName)) return abCache[abName];
            else
            {
    
    
                ab = AssetBundle.LoadFromFile(basePath + abName);
                abCache.Add(abName, ab);
                return ab;
            }


        }
        
        
        //==================三种资源同步加载方式==================
        //提供多种调用方式 便于其它语言的调用(Lua对泛型支持不好)
        #region 同步加载的三个重载
        
        /// <summary>
        /// 同步加载资源---泛型加载 简单直观 无需显示转换
        /// </summary>
        /// <param name="abName">ab包的名称</param>
        /// <param name="resName">资源名称</param>
        public T LoadResource<T>(string abName,string resName)where T:Object
        {
    
    
            //加载目标包
            AssetBundle ab = LoadABPackage(abName);

            //返回资源
            return ab.LoadAsset<T>(resName);
        }

        
        //不指定类型 有重名情况下不建议使用 使用时需显示转换类型
        public Object LoadResource(string abName,string resName)
        {
    
    
            //加载目标包
            AssetBundle ab = LoadABPackage(abName);

            //返回资源
            return ab.LoadAsset(resName);
        }
        
        
        //利用参数传递类型,适合对泛型不支持的语言调用,使用时需强转类型
        public Object LoadResource(string abName, string resName,System.Type type)
        {
    
    
            //加载目标包
            AssetBundle ab = LoadABPackage(abName);

            //返回资源
            return ab.LoadAsset(resName,type);
        }

        #endregion
        
        
        //================三种资源异步加载方式======================

        /// <summary>
        /// 提供异步加载----注意 这里加载AB包是同步加载,只是加载资源是异步
        /// </summary>
        /// <param name="abName">ab包名称</param>
        /// <param name="resName">资源名称</param>
        public void LoadResourceAsync(string abName,string resName, System.Action<Object> finishLoadObjectHandler)
        {
    
    
            AssetBundle ab = LoadABPackage(abName);
            //开启协程 提供资源加载成功后的委托
            StartCoroutine(LoadRes(ab,resName,finishLoadObjectHandler));
        }

        
        private IEnumerator LoadRes(AssetBundle ab,string resName, System.Action<Object> finishLoadObjectHandler)
        {
    
    
            if (ab == null) yield break;
            //异步加载资源API
            AssetBundleRequest abr = ab.LoadAssetAsync(resName);
            yield return abr;
            //委托调用处理逻辑
            finishLoadObjectHandler(abr.asset);
        }
        
        
        //根据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));
        }
        
       	
        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;
            //委托调用处理逻辑
            finishLoadObjectHandler(abr.asset);
        }

        
        //泛型加载
        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;
            //委托调用处理逻辑
            finishLoadObjectHandler(abr.asset as T);
        }


        //====================AB包的两种卸载方式=================
        //单个包卸载
        public void UnLoad(string abName)
        {
    
    
            if(abCache.ContainsKey(abName))
            {
    
    
                abCache[abName].Unload(false);
                //注意缓存需一并移除
                abCache.Remove(abName);
            }
        }

        //所有包卸载
        public void UnLoadAll()
        {
    
    
            AssetBundle.UnloadAllAssetBundles(false);
            //注意清空缓存
            abCache.Clear();
            mainAB = null;
            mainManifest = null;
        }
    }
}

La clase singleton de script heredada por el administrador anterior es la siguiente

MonoSingleton.cs

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

namespace Common
{
    
    
    ///<summary>
    ///脚本单例类,负责为唯一脚本创建实例
    ///<summary>

    public class MonoSingleton<T> : MonoBehaviour where T:MonoSingleton<T> //注意此约束为T必须为其本身或子类
    {
    
    
        /*
        相较于直接在需要唯一创建的脚本中创建实例,Awake初始化的过程需要解决的问题
        1.代码重复
        2.在Awake里面初始化,其它脚本在Awake中调用其可能会为Null的异常情况
         */

        //解决1:使用泛型创建实例   解决2:使用按需加载(即有其它脚本调用时在get中加载)

        private static T instance; //创建私有对象记录取值,可只赋值一次避免多次赋值

        public static T Instance
        {
    
    
            //实现按需加载
            get
            {
    
    
                //当已经赋值,则直接返回即可
                if (instance != null) return instance;

                instance = FindObjectOfType<T>();

                //为了防止脚本还未挂到物体上,找不到的异常情况,可以自行创建空物体挂上去
                if (instance == null)
                {
    
    
                    //如果创建对象,则会在创建时调用其身上脚本的Awake即调用T的Awake(T的Awake实际上是继承的父类的)
                    //所以此时无需为instance赋值,其会在Awake中赋值,自然也会初始化所以无需init()
                    /*instance = */
                    new GameObject("Singleton of "+typeof(T)).AddComponent<T>();
                }
                else instance.Init(); //保证Init只执行一次

                return instance;

            }
        }

        private void Awake()
        {
    
    
            //若无其它脚本在Awake中调用此实例,则可在Awake中自行初始化instance
            instance = this as T;
            //初始化
            Init();
        }

        //子类对成员进行初始化如果放在Awake里仍会出现Null问题所以自行制作一个init函数解决(可用可不用)
        protected virtual void Init()
        {
    
    

        }
    }

}

Supongo que te gusta

Origin blog.csdn.net/weixin_45136016/article/details/133126168
Recomendado
Clasificación