Explicação detalhada do pacote AB no Unity

0. Link de referência

Link do artigo original do autor: https://blog.csdn.net/Q540670228/article/details/122795671

1. Conceito de AssetBundle

AssetBundle, também conhecido como pacote AB, é um pacote de compactação de recursos fornecido pelo Unity para armazenar recursos.

O sistema AssetBundle no Unity é uma extensão do gerenciamento de recursos. Ao distribuir recursos em diferentes pacotes AB, ele pode minimizar a pressão da memória em tempo de execução. Ele pode carregar e descarregar pacotes AB dinamicamente e, em seguida, carregar conteúdo seletivamente.

Vantagens do AssetBundle:

  1. O local de armazenamento do pacote AB é personalizado e pode então ser colocado em um caminho legível e gravável para facilitar atualizações importantes.
  2. Método de compactação personalizado de pacote AB, você pode escolher sem compactação ou escolher métodos de compactação como LZMA e LZ4 para reduzir o tamanho do pacote e permitir uma transmissão de rede mais rápida.
  3. Os recursos podem ser distribuídos em diferentes pacotes AB para minimizar a pressão da memória durante o tempo de execução e podem ser carregados imediatamente e carregar seletivamente o conteúdo necessário.
  4. Os pacotes AB suportam atualizações dinâmicas em um estágio posterior, reduzindo significativamente o tamanho do pacote de instalação inicial. Recursos não essenciais são carregados no servidor na forma de pacotes AB e carregados dinamicamente durante execuções posteriores para melhorar a experiência do usuário.

2. Comparação entre AssetBundle e Recursos

Pacote de ativos Recursos
Os recursos podem ser distribuídos em vários pacotes Todos os recursos são agrupados em um grande pacote
Personalização flexível do local de armazenamento Deve ser armazenado no diretório Resources
Métodos de compressão flexíveis (LZMA, LZ4) Todos os recursos serão compactados em binário
Suporta atualizações dinâmicas posteriormente Após o empacotamento, os recursos são somente leitura e não podem ser alterados dinamicamente.

3. Características do AssetBundle

  • O pacote AB pode armazenar a maioria dos recursos do Unity, mas não pode armazenar scripts C# diretamente, portanto, as atualizações de código precisam usar Lua ou armazenar arquivos DLL compilados.
  • O pacote AB não pode ser carregado repetidamente. Quando o pacote AB é carregado na memória, ele deve ser descarregado antes de ser recarregado.
  • Vários recursos são distribuídos em diferentes pacotes AB. Alguns recursos, como a textura de um pré-fabricado, podem não estar no mesmo pacote. O carregamento direto pode causar a perda de alguns recursos. Ou seja, há dependências entre os pacotes AB. Ao carregar o atual Os pacotes AB precisam carregar juntos os pacotes dos quais dependem.
  • Após a conclusão do empacotamento, um pacote principal será gerado automaticamente (o nome do pacote principal varia de acordo com as diferentes plataformas). O número da versão, o código de verificação (CRC) e todas as outras informações relacionadas ao pacote (nomes, dependências) serão armazenadas no manifesto do pacote principal.)

Insira a descrição da imagem aqui

4. Processo de empacotamento completo do AssetBundle

4.1 Obtendo o plug-in AssetBundleBrowser

Método 1: a versão Unity 2019 pode encontrar este plug-in diretamente no Windows -> PackageManager e instalá-lo diretamente
Insira a descrição da imagem aqui

Após a versão 2020 ou outras versões, talvez você não consiga encontrar este plug-in no método 1. Você pode pesquisar e baixar o pacote compactado no github. O endereço de download: https://github.com/Unity-Technologies/AssetBundles -O navegador
Insira a descrição da imagem aqui
irá baixar o pacote de instalação. Descompacte-o na pasta Pacotes do projeto Unity (certifique-se de descompactá-lo)
Insira a descrição da imagem aqui

4.2 Uso do painel AssetBundleBrowser

  • Painel de configuração: pode visualizar a situação básica do pacote AB atual e seus recursos internos (tamanho, recursos, dependências, etc.)
    Insira a descrição da imagem aqui
  • Painel Build: Responsável pelas configurações relevantes do pacote AssetBundle. Pressione Build to package.
    Insira a descrição da imagem aqui
    Três métodos de compactação.
  1. NoCompression: Sem compactação, descompactação rápida, tamanho de pacote grande, não recomendado.
  2. LZMA: possui a menor compactação e descompactação lenta. Para usar um recurso, você precisa descompactar todos os recursos do pacote.
  3. LZ4: A compactação é um pouco maior, a descompactação é mais rápida, você pode usar o que quiser para descompactar e o uso de memória é baixo, é mais recomendado.
    Geralmente, as configurações que precisam ser alteradas são as configurações de opções relevantes verificadas na imagem.
  • Painel de inspeção: usado principalmente para visualizar alguns detalhes do arquivo do pacote AB empacotado (tamanho, caminho do recurso, etc.)
    Insira a descrição da imagem aqui

4.3 Defina o pacote AssetBundle ao qual o recurso pertence

Insira a descrição da imagem aqui
Na parte inferior do painel Inspetor do recurso que precisa ser empacotado, você pode selecionar em qual pacote AB ele deve ser colocado. Você também pode criar um novo pacote AB para colocar o recurso nele. Depois de colocá-lo, construa o pacote novamente para colocar o recurso no pacote AB correspondente.

5. Gerenciador de AssetBundle

O principal requisito do gerenciador de pacotes AB: carregar os recursos especificados no pacote AB especificado. As etapas básicas são as seguintes:

  1. Carregue o pacote AB onde o recurso está localizado e todos os seus pacotes dependentes (encontre o nome do pacote dependente de acordo com as informações do manifesto do pacote principal)
  2. Carregue o recurso especificado do pacote AB (de acordo com o nome, tipo)
  3. Descarregue o pacote AB carregado quando não estiver mais em uso.
    Principais APIs 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包

A seguir está o código detalhado e os comentários do gerenciador de pacotes AB. O conteúdo do modo singleton envolvido será brevemente apresentado mais tarde.

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;
        }
    }
}

A classe singleton de script herdada pelo gerenciador acima é a seguinte

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()
        {
    
    

        }
    }

}

Acho que você gosta

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