Unity热更新 之 Addressables(1) 资源基础加载
时隔多日,咩咩正式开启网络与热更新部分的学习,劲啊 很劲啊!
本文内容整合包括但不限于 Unity唐老狮,Unity官方手册,部分网络资源与AI工具
仅作学习笔记交流,不做任何商业用途,侵权删
Addressables是unity官方提供,可以理解为对AssetBundle进行封装与优化的可视化工具, 所以需要对AssetBundle有一定了解,见下链接:Unity 热更新 之 一篇文章完全入门AssetBundle-CSDN博客
版本:Unity6
模板:任意(演示模板为:3D核心 渲染管线为URP)
学习前提:有AB包基础,知道委托和事件,知道匿名函数怎么用
加油站:C# & Unity 面向对象补全计划 之 委托-CSDN博客
C# & Unity 面向对象补全计划 之 事件-CSDN博客
C# & Unity 面向对象补全计划 之 匿名函数与Lambda表达式_unity 匿名函数-CSDN博客
目录
1.导入包
2.简单使用方法
3.加载资源重要API
3.1 AssetReference(仅作了解即可)
3.2 资源加载完成回调与状态判别
3.3动态加载单资源
3.4 动态加载多资源
3.5 多资源加载筛选问题
3.6资源释放
4.练习题
1.导入包
不墨迹
工具的窗口路径 为什么中英文掺杂?
我为了看最新的组件改了中文,但是日常使用为英文,编辑器内改语言就会出现这种bug
2.简单使用方法
基础界面 没什么好介绍的,这个工具的英文十分简单,不会的拿翻译软件拍照翻译即可
以预制体举例添加资源到其中:
打勾之后点确定,会出现一个新的文件夹Resources_moved
原因:热更新的包是单独打出去的,而放到Resources文件夹会和unity一同打包出去,所以不合适,系统就会创建一个新的文件夹
看到如下界面
你可以点New 创建新的包,(包名自定义)
然后把资源放里面去,就相当于AssetBundle里面的包的概念
你可能发现你的资源名字很长,不要紧 右键点击简化可寻址资源名即可
可以给资源打标签,一个资源可以有多个标签
3.加载资源重要API
3.1 AssetReference(仅作了解即可)
AssetReference可以理解为Addressables中的资源标识类,他可以存储对不同可寻址资源的引用,并且通过它提供的方法(如加载、实例化资源等操作)来使用这些资源
举个例子:
声明一个该变量
public AssetReference anyTypeAsset;
就可以从外部去拖
可以接受的内容包含了整个可寻址资源的资产
可以泛型加载,且通常是异步加载(异步加载的本质是后台开新线程去做,所以至少要等主进程的一帧后才会完成),其有返回值AsyncOperationHandle<TObject>
//加载资源会有AsyncOperationHandle 返回值
AsyncOperationHandle<GameObject> returnObj = anyTypeAsset.LoadAssetAsync<GameObject>();
下面将会对returnObj进行操作
3.2 资源加载完成回调与状态判别
资源加载后的返回值有Completed事件
注意描述:Completion event for the internal operation. If this is assigned on a completed operation, the callback is deferred until the LateUpdate of the current frame.
内部操作的完成事件。如果在一个已完成的操作上赋值,回调将被延迟到当前帧
当这个事件被完成之后无论成功与失败都会执行回调函数,之后通过状态判断资源是否被加载成功
returnObj.Completed += Callback;
private void Callback(AsyncOperationHandle<GameObject> obj) {
if (obj.Status == AsyncOperationStatus.Succeeded) {
Instantiate(obj.Result);
}
else if (obj.Status == AsyncOperationStatus.Failed) {
Debug.Log("资源加载失败");
}
}
其中状态是一个枚举
另外上述的代码可以用匿名函数的方式 一步到位
anyTypeAsset.LoadAssetAsync<GameObject>().Completed += ((obj) => {
if (obj.Status == AsyncOperationStatus.Succeeded) {
Instantiate(obj.Result);
}
else if (obj.Status == AsyncOperationStatus.Failed) {
Debug.Log("资源加载失败");
}
});
3.3动态加载单资源
在实际项目之中,3.1并不常用,而3.3和3.4两个部分的api用是非常多的,其实也非常简单
就是用Addressables进行异步加载,也是可以通过泛型进行类型的准确加载,但是可以通过标签去加载
Addressables.LoadAssetAsync<GameObject>("Cube");
Addressables.LoadAssetAsync<GameObject>("A");
返回值还是 AsyncOperationHandle,所以3.2的资源加载的回调和状态判别仍旧是一样的
Addressables.LoadAssetAsync<GameObject>("A").Completed += ((obj) => {
if (obj.Status == AsyncOperationStatus.Succeeded) {
Instantiate(obj.Result);
}
else if (obj.Status == AsyncOperationStatus.Failed) {
Debug.Log("资源加载失败");
}
});
那么有趣的来了,如果标签一样那么其会加载哪一个?
是你该组别中的第一个有该标签的资源 这个时候就需要动态加载多个资源的方法了
3.4 动态加载多资源
Addressables.LoadAssetAsync
Addressables.LoadAssetsAsync,多资源比单资源加载就多个s,返回值就变成了 <IList<TObject>>
public static AsyncOperationHandle<IList<TObject>> LoadAssetsAsync<TObject>(string key, Action<TObject> callback = null)
{
return m_Addressables.LoadAssetsAsync(new List<string>() { key }, callback, MergeMode.None, true);
}
//加载多个资源,受到泛型约束
AsyncOperationHandle<IList<GameObject>> handle = Addressables.LoadAssetsAsync<GameObject>("A");
handle.Completed += (obj) => {
foreach (var item in handle.Result) {
if (obj.Status == AsyncOperationStatus.Succeeded) {
Instantiate(item);
}
else if (obj.Status == AsyncOperationStatus.Failed) {
Debug.Log("资源加载失败");
}
}
};
来看一下结果:
3.5 多资源加载筛选问题
多资源加载有个重要的重载,其诞生的原因为多资源中名字与标签的重合问题,就比如我的包有如下内容排布 ,这样只通过包和标签去加载的话就成了问题
比如用两个Addressables.LoadAssetsAsync<T>去加载,或者先全部加载成Objcet后再分别装箱拆箱区分,这样十分麻烦
那么就会用以下重载
List<string> strs = new List<string>() { "Cube", "A" };
Addressables.LoadAssetsAsync<Object>(strs, (obj) => {
print(obj.name);
}, Addressables.MergeMode.Intersection);
//参数一:想要加载资源的条件列表(资源名、Lable名)
//参数二:每个加载资源结束后会调用的函数,会把加载到的资源传入该函数中
//参数三:可寻址的合并模式,用于合并请求结果的选项。
//如果键(Cube,A)映射到结果([1,2,3,4],[1,3,4]),数字代表不同的资源
//None:不发生合并,将使用第一组结果 结果为[1,2,3,4]
//UseFirst:应用第一组结果 结果为[1,2,3,4]
//Union:合并所有结果 结果为[1,2,3,4]
//Intersection:使用相交结果 结果为[1,3,4]
//参数四:如果为true表示当资源加载失败时,会自动将已加载的资源和依赖都释放掉
// 如果为false,需要自己手动来管理释放
3.6资源释放
这里需要注意的一点就是,释放的是资源异步加载后的返回值
AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("Cube");
Addressables.Release(handle);
4.练习题
唐老师的对3.4与3.5的封装 ,仅供参考
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class Exercises : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
#region Lesson5 练习题
//尝试自己写一个Addressables资源管理器
//帮助我们通过名字加载单个资源或场景,并管理资源相关内容
//AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("Cube");
//handle.Completed += (obj) => {
// //加载成功后的逻辑处理
// Addressables.Release(obj);
//};
//分析:
//1.资源动态加载可以通过返回的对象AsyncOperationHandle<>(异步操作句柄) 添加完成监听来处理加载后的资源
//2.释放资源时也是通过释放返回的对象AsyncOperationHandle<>(异步操作句柄) 来进行释放
//3.如果分散在各脚本中自己管理资源难免显得太过凌乱,所以我们可以通过一个资源管理器来管理所有的异步加载返回对象AsyncOperationHandle<>(异步操作句柄)
//所以如果我们要自己写一个Addressables资源管理器,主要就是用来管理AsyncOperationHandle<>对象的
//AddressablesMgr.Instance.LoadAssetAsync<GameObject>("Cube", (obj) =>
//{
// Instantiate(obj.Result);
//});
//AddressablesMgr.Instance.LoadAssetAsync<GameObject>("Cube", (obj) =>
//{
// Instantiate(obj.Result, Vector3.right * 5, Quaternion.identity);
// //使用完资源后 移除资源
// AddressablesMgr.Instance.Release<GameObject>("Cube");
//});
#endregion
#region Lesson6 练习题
//尝试在上一个Addressables资源管理器中
//提供一个批量或指定加载释放资源的方法
//List<string> strs = new List<string>() { "Cube", "HD" };
//Addressables.LoadAssetsAsync<Object>(strs, (obj) => {
// print(obj.name);
//}, Addressables.MergeMode.Intersection);
//AddressablesMgr.Instance.LoadAssetAsync<Object>(Addressables.MergeMode.Intersection, (obj) =>
//{
// print("1" + obj.name);
//}, "Cube", "SD");
//AddressablesMgr.Instance.LoadAssetAsync<Object>(Addressables.MergeMode.Intersection, (obj) =>
//{
// print("2" + obj.name);
//}, "Cube", "SD");
#endregion
}
// Update is called once per frame
void Update()
{
//创建对象 记录异步操作句柄
if (Input.GetKeyDown(KeyCode.Space))
{
AddressablesMgr.Instance.LoadAssetAsync<GameObject>("Cube", (obj) =>
{
Instantiate(obj.Result);
});
}
//从创建对象中 释放异步操作句柄资源
if (Input.GetKeyDown(KeyCode.Q))
{
AddressablesMgr.Instance.Release<GameObject>("Cube");
}
}
}