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包及其资源
cs
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包获取模型材质和模型
方法二
这里利用主包的依赖性

cs
//依赖包的关键知识点---利用主包 获取依赖信息
//加载主包
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
csstring[] strs = mainABManifest.GetAllDependencies(abName);abName为model(即找到model包中的依赖关系并传递给strs函数),strs参数中的数据为mateia。
AB包资源加载管理器
首先需要注意:同步加载要有三种方法:泛型方法同步加载与普通参数同步加载
额外需要注意:(Lua中不支持泛型方法,所以可能会用Type形式)
同步加载
优化端:
反正三种方法都要完成如下操作:
加载对应包------查找对应包中的依赖关系------加载对应依赖包------记录加载过的包
不如将这套流程打包成一个函数在三个方法中调用
cs
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);
}
}
不指定类型的同步加载
cs
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;
}
}
指定类型的同步加载
cs
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存储是红色的球,所以会加载一个红色的球
cs
public class ABTest : MonoBehaviour
{
void Start()
{
GameObject go = ABMgr.Instance.LoadRes("model", "Sphere",typeof(GameObject)) as GameObject;
go.transform.position = Vector3.up;
}
}
泛型的同步加载
cs
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)
不指定类型的异步加载
cs
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
调用对应方法:

指定类型异步加载
cs
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));
}
}
根据泛型加载资源
cs
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));
}
}





