Lua与AB包/Addressables以及YooAsset

摘自千问:

  1. Lua 是菜谱(逻辑):决定了菜怎么做,味道如何。因为你需要随时换菜谱(热更新),所以菜谱不能死板地印在墙上(编译进主包)。
  2. AssetBundle 是外卖盒(格式):你把写好的菜谱(Lua 文件)装进外卖盒里,方便运输。
  3. Addressables / YooAsset 是配送系统(管理)
    • 它们负责去仓库(服务器)拿外卖盒。
    • 检查盒子是不是最新的(版本管理)。
    • 把盒子送到厨房(内存),并拆开盒子把菜谱拿出来交给厨师(Lua 虚拟机)。

值得注意的是,现在除了 Lua,还有一种流行的热更方案是 HybridCLR(原 ilruntime/huohuo 的替代者)。

  • 如果你用 HybridCLR,你的逻辑是 C# 写的,那么 Lua 的地位就会下降,可能只用来写简单的 UI 脚本或配置文件。

AB包相关概念

热更新基本规则:

AB包浏览器插件的安装

AB包浏览器文件的安装https://blog.csdn.net/qq_36303853/article/details/148812182#t5

资源概念

        C#代码不能放到AB包,因为C#是编译型语言,我们才需要脚本语言lua

        Unity 编辑器通过反射,实现了对预设体上组件的动态识别、属性展示——这就是为什么Csharp脚本文件不能进AB包而组件能进AB包的原因

预制体相关概念https://blog.csdn.net/2303_80204192/article/details/157287772#t1

AB包打包流程

1.给资源划分包

        如果模型上有其他主包中的资源,在打包时会构建依赖关系

2.打包页面

打包后对应文件夹就会出现

build页签中的相关属性

AB包资源加载

都是先加载AB包,再加载AB包中资源

1.AB包的同步加载:

 void Start()
    {
        //加载AB包
        AssetBundle ab=AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/model");
        //加载资源 一种泛型,一种非泛型
        GameObject go=ab.LoadAsset<GameObject>("Sphere");
        GameObject go2 =ab.LoadAsset("Sphere", typeof(GameObject)) as GameObject;

        //实例化
        Instantiate(go);
    }

        注意:AB包不能重复加载,否则会报错!

2.AB包的异步加载(利用协程)

void Start()
    {
        // 开始协程,加载 UI 精灵
        StartCoroutine(ABResLoad("head", "ui_DL_an_queding_01"));
    }

IEnumerator ABResLoad(string ABname, string resName)
    {
        // 加载 AB 包
        AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABname);
        yield return abcr;

        // 加载资源(Sprite)
        AssetBundleRequest abr = abcr.assetBundle.LoadAssetAsync<Sprite>(resName);
        yield return abr;

        img.sprite = abr.asset as Sprite;
    }

3.卸载AB包及其资源

void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // 卸载所有加载的 AB 包,参数为 true 会把通过 AB 包加载的资源也卸载
            AssetBundle.UnloadAllAssetBundles(false);

            //卸载单个资源
            AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/model");
            // 加载资源(泛型和非泛型两种写法)
            GameObject go = ab.LoadAsset<GameObject>("Sphere");
            // 卸载单个 AB 包
            ab.Unload(false);
        }
    }

        True:AB包以及资源一起卸载 ;False:AB包卸载,资源不会卸载

AB包的依赖

        如果一个模型在A包中,其材质在B包中,那么只加载A包中的模型资源会丢失材质

方法一

        同时加载AB包获取模型材质和模型

方法二

这里利用主包的依赖性

//依赖包的关键知识点—利用主包 获取依赖信息
//加载主包
AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "PC");
//加载主包中的固定文件
AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
//从固定文件中 得到依赖信息
string[] strs = abManifest.GetAllDependencies("model");
//得到了 依赖包的名字
for (int i = 0; i < strs.Length; i++)
{
    Debug.Log(strs[i]);
}

ManiFest文件中的依赖关系如下:

缺陷:

主包依赖关系文件只能记录包与包之间的依赖关系,至于包中资源具体依赖关系是不知道的

        比如A包中a资源依赖B包C包,A包中b资源需要D包E包,使用如上代码会导致加载a资源时,也会加载不需要的D包E包        

GetAllDependencies

获取Manifest文件中的与对应包中的依赖关系

我这里设定一个红色球,球的模型放在model包中,材质放在materia包中

添加然后根据AB包打包流程打包情况如下

调用API

string[] strs = mainABManifest.GetAllDependencies(abName);

        abName为model(即找到model包中的依赖关系并传递给strs函数),strs参数中的数据为mateia。

AB包资源加载管理器

首先需要注意:同步加载要有三种方法:泛型方法同步加载与普通参数同步加载

额外需要注意:(Lua中不支持泛型方法,所以可能会用Type形式)

同步加载

优化端:

        反正三种方法都要完成如下操作:

加载对应包——查找对应包中的依赖关系——加载对应依赖包——记录加载过的包

        不如将这套流程打包成一个函数在三个方法中调用

 public void LoadAB(string abName)
    {
        //加载主包及主包的依赖关系
        if(mainAB==null)
        {
            mainAB = AssetBundle.LoadFromFile(pathUrl + MainABName);
            mainABManifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }

        //获取依赖关系
        string[] strs = mainABManifest.GetAllDependencies(abName);
        for(int i=0;i<strs.Length;i++)
        {
            //如果AB包的依赖包没有加载过,则加载并且记录
            if(!abDic.ContainsKey(strs[i]))
            {
                AssetBundle ab = AssetBundle.LoadFromFile(pathUrl + strs[i]);
                abDic.Add(strs[i],ab);
            }
        }

        //加载资源来源包
        //如果AB包没有加载过,则加载并且记录
        if(!abDic.ContainsKey(abName))
        {
            AssetBundle ab = AssetBundle.LoadFromFile(pathUrl + abName);
            abDic.Add(abName,ab);
        }
    }

不指定类型的同步加载

public object LoadRes(string abName,string assetName)
    {
        //加载资源
        LoadAB(abName);
        //此处设计优化了一下:在加载的同时判断是否为object类型,如果是object类型则直接实例化后返回
        Object obj = abDic[abName].LoadAsset(assetName);
        if(obj is GameObject)
        {
            return Instantiate(obj);
        }
        else
        {
            return obj;
        }
    }

指定类型的同步加载

public object LoadRes(string abName,string assetName,System.Type type)
    {
        //加载资源
        LoadAB(abName);
        //此处设计优化了一下:在加载的同时判断是否为object类型,如果是object类型则直接实例化后返回
        Object obj = abDic[abName].LoadAsset(assetName,type);
        if(obj is GameObject)
        {
            return Instantiate(obj);
        }
        else
        {
            return obj;
        }
    }

外部调用该函数举例:

        这里的model包中的Sphere存储是红色的球,所以会加载一个红色的球

public class ABTest : MonoBehaviour
{

    void Start()
    {
        GameObject go = ABMgr.Instance.LoadRes("model", "Sphere",typeof(GameObject)) as GameObject;
        go.transform.position = Vector3.up;
 
    }
}

        

泛型的同步加载

public T LoadRes<T>(string abName,string assetName) where T:Object
    {
        //加载资源
        LoadAB(abName);
        //此处设计优化了一下:在加载的同时判断是否为object类型,如果是object类型则直接实例化后返回
        T obj = abDic[abName].LoadAsset<T>(assetName);
        if(obj is GameObject)
        {
            return Instantiate(obj) as T;
        }
        else
        {
            return obj as T;
        }
    }

异步加载

        这里需要注意:AB包的加载不适用异步加载,只有加载资源时才使用异步加载

        与同步加载相似,也有三种加载方法

        加载AB包以及AB包依赖关系与同步加载共用APi:LoadAB(abName)

不指定类型的异步加载

public void LoadResAsync(string abName,string assetName,UnityAction<Object> callback)
    {
        StartCoroutine(LoadResAsyncCoroutine(abName,assetName,callback));
    }
    

    private IEnumerator LoadResAsyncCoroutine(string abName,string assetName,UnityAction<Object> callback)
    {
        //加载AB包
        LoadAB(abName);
        //异步加载资源
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync(assetName);
        yield return abr;
        //此处设计优化了一下:在加载的同时判断是否为object类型,如果是object类型则直接实例化后返回
        Object obj = abr.asset;
        if(obj is GameObject)
        {
            callback(Instantiate(obj));
        }
    }

对传参UnityAction<T>不太了解,具体原理请看:

UnityAction https://blog.csdn.net/2303_80204192/article/details/157148540#t3

调用对应方法:

指定类型异步加载

public void LoadResAsync(string abName,string assetName,System.Type type,UnityAction<Object> callback)
    {
        StartCoroutine(LoadResAsyncCoroutine(abName,assetName,type,callback));
    }

    public IEnumerator LoadResAsyncCoroutine(string abName,string assetName,System.Type type,UnityAction<Object> callback)
    {
        //加载AB包
        LoadAB(abName);
        //异步加载资源
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync(assetName,type);
        yield return abr;
        //此处设计优化了一下:在加载的同时判断是否为object类型,如果是object类型则直接实例化后返回
        Object obj = abr.asset;
        if(obj is GameObject)
        {
            callback(Instantiate(obj));
        }
    }

根据泛型加载资源

public void LoadResAsync<T>(string abName,string assetName,UnityAction<Object> callback) where T:Object
    {
        StartCoroutine(LoadResAsyncCoroutine<T>(abName,assetName,callback));
    }

    public IEnumerator LoadResAsyncCoroutine<T>(string abName,string assetName,UnityAction<Object> callback) where T:Object
    {
        //加载AB包
        LoadAB(abName);
        //异步加载资源
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync<T>(assetName);
        yield return abr;
        //此处设计优化了一下:在加载的同时判断是否为object类型,如果是object类型则直接实例化后返回
        Object obj = abr.asset;
        if(obj is GameObject)
        {
            callback(Instantiate(obj));
        }
    }

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐