Lua与AB包/Addressables以及YooAsset摘自千问Lua是菜谱逻辑决定了菜怎么做味道如何。因为你需要随时换菜谱热更新所以菜谱不能死板地印在墙上编译进主包。AssetBundle是外卖盒格式你把写好的菜谱Lua 文件装进外卖盒里方便运输。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#是编译型语言我们才需要脚本语言luaUnity 编辑器通过反射实现了对预设体上组件的动态识别、属性展示——这就是为什么Csharp脚本文件不能进AB包而组件能进AB包的原因预制体相关概念https://blog.csdn.net/2303_80204192/article/details/157287772#t1AB包打包流程1.给资源划分包如果模型上有其他主包中的资源在打包时会构建依赖关系2.打包页面打包后对应文件夹就会出现build页签中的相关属性AB包资源加载都是先加载AB包再加载AB包中资源1.AB包的同步加载void Start() { //加载AB包 AssetBundle abAssetBundle.LoadFromFile(Application.streamingAssetsPath /model); //加载资源 一种泛型一种非泛型 GameObject goab.LoadAssetGameObject(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.LoadAssetAsyncSprite(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.LoadAssetGameObject(Sphere); // 卸载单个 AB 包 ab.Unload(false); } }TrueAB包以及资源一起卸载 FalseAB包卸载资源不会卸载AB包的依赖如果一个模型在A包中其材质在B包中那么只加载A包中的模型资源会丢失材质方法一同时加载AB包获取模型材质和模型方法二这里利用主包的依赖性//依赖包的关键知识点—利用主包 获取依赖信息 //加载主包 AssetBundle abMain AssetBundle.LoadFromFile(Application.streamingAssetsPath / PC); //加载主包中的固定文件 AssetBundleManifest abManifest abMain.LoadAssetAssetBundleManifest(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包打包流程打包情况如下调用APIstring[] strs mainABManifest.GetAllDependencies(abName);abName为model即找到model包中的依赖关系并传递给strs函数strs参数中的数据为mateia。AB包资源加载管理器首先需要注意同步加载要有三种方法泛型方法同步加载与普通参数同步加载额外需要注意Lua中不支持泛型方法所以可能会用Type形式同步加载优化端反正三种方法都要完成如下操作加载对应包——查找对应包中的依赖关系——加载对应依赖包——记录加载过的包不如将这套流程打包成一个函数在三个方法中调用public void LoadAB(string abName) { //加载主包及主包的依赖关系 if(mainABnull) { mainAB AssetBundle.LoadFromFile(pathUrl MainABName); mainABManifest mainAB.LoadAssetAssetBundleManifest(AssetBundleManifest); } //获取依赖关系 string[] strs mainABManifest.GetAllDependencies(abName); for(int i0;istrs.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 LoadResT(string abName,string assetName) where T:Object { //加载资源 LoadAB(abName); //此处设计优化了一下在加载的同时判断是否为object类型如果是object类型则直接实例化后返回 T obj abDic[abName].LoadAssetT(assetName); if(obj is GameObject) { return Instantiate(obj) as T; } else { return obj as T; } }异步加载这里需要注意AB包的加载不适用异步加载只有加载资源时才使用异步加载与同步加载相似也有三种加载方法加载AB包以及AB包依赖关系与同步加载共用APiLoadABabName不指定类型的异步加载public void LoadResAsync(string abName,string assetName,UnityActionObject callback) { StartCoroutine(LoadResAsyncCoroutine(abName,assetName,callback)); } private IEnumerator LoadResAsyncCoroutine(string abName,string assetName,UnityActionObject 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)); } }对传参UnityActionT不太了解具体原理请看UnityActionhttps://blog.csdn.net/2303_80204192/article/details/157148540#t3调用对应方法指定类型异步加载public void LoadResAsync(string abName,string assetName,System.Type type,UnityActionObject callback) { StartCoroutine(LoadResAsyncCoroutine(abName,assetName,type,callback)); } public IEnumerator LoadResAsyncCoroutine(string abName,string assetName,System.Type type,UnityActionObject 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 LoadResAsyncT(string abName,string assetName,UnityActionObject callback) where T:Object { StartCoroutine(LoadResAsyncCoroutineT(abName,assetName,callback)); } public IEnumerator LoadResAsyncCoroutineT(string abName,string assetName,UnityActionObject callback) where T:Object { //加载AB包 LoadAB(abName); //异步加载资源 AssetBundleRequest abr abDic[abName].LoadAssetAsyncT(assetName); yield return abr; //此处设计优化了一下在加载的同时判断是否为object类型如果是object类型则直接实例化后返回 Object obj abr.asset; if(obj is GameObject) { callback(Instantiate(obj)); } }