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#是编译型语言,我们才需要脚本语言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>不太了解,具体原理请看:
UnityActionhttps://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)); } }