Unity 之 可寻址系统 -- 代码加载介绍 -- 进阶(一)
- 一,可寻址系统代码加载
- 1.1 回调形式
- 1.2 异步等待
- 1.3 面板赋值
- 1.4 同步加载
- 二,可寻址系统分标签加载
- 2.1 场景搭建
- 2.2 代码示例
- 2.3 效果展示
- 三,代码加载可寻址的解释
概述:本片文章为大家介绍可寻址系统使用代码动态加载物体的多种形式。
一,可寻址系统代码加载
准备工作,创建几个预制体分别为:Cube
,Capsule
,Sphere
,并将预制体设置为可寻址系统的资源,然后将Cube的地址修改为Cube
,如下图:
1.1 回调形式
using UnityEngine;
// 引用命名空间
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class LoadManager : MonoBehaviour
{
void Start()
{
// 回调形式
LoadGameObject();
LoadGameObjectCallBack();
InstantiateGameObject();
}
# region 回调形式
/// <summary>
/// 加载物体
/// 逻辑简单且不需复用,直接使用Lambda表达式的形式
/// </summary>
void LoadGameObject()
{
// 参数:"Cube" 为可寻址系统的地址
Addressables.LoadAssetAsync<GameObject>("Cube").Completed += (obj) =>
{
GameObject go = obj.Result;
Instantiate(go, Vector3.zero, Quaternion.identity);
};
}
/// <summary>
/// 加载物体
/// </summary>
void LoadGameObjectCallBack()
{
Addressables.LoadAssetAsync<GameObject>("Assets/Prefab/Sphere.prefab").Completed += LoadCallBack;
}
/// <summary>
/// 加载物体的回调函数
/// </summary>
void LoadCallBack(AsyncOperationHandle<GameObject> handle)
{
GameObject go = handle.Result;
Instantiate(go, Vector3.right * 2, Quaternion.identity);
}
/// <summary>
/// 加载并实例化物体
/// </summary>
void InstantiateGameObject()
{
Addressables.InstantiateAsync("Assets/Prefab/Capsule.prefab").Completed += (obj) =>
{
// 已经实例化后的物体
GameObject go = obj.Result;
go.transform.position = Vector3.left * 2;
};
}
#endregion
}
运行结果如下:
成功加载三个预制
1.2 异步等待
若你不习惯回调的形式,可寻址系统也给了我们加载异步等待的形式直接去加载物体,示例代码如下:
using UnityEngine;
// 引用命名空间
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class LoadManager : MonoBehaviour
{
void Start()
{
// 异步形式
AsyncLoadCube();
AsyncInstantiateCube();
}
#region 异步形式
private async void AsyncLoadCube()
{
// 虽然这里使用了Task,但并没有使用多线程
GameObject prefabObj = await Addressables.LoadAssetAsync<GameObject>("Assets/Prefabs/Cube.prefab").Task;
// 实例化
GameObject cubeObj = Instantiate(prefabObj);
cubeObj.transform.position = Vector3.zero;
}
private async void AsyncInstantiateCube()
{
// 直接使用InstantiateAsync方法
GameObject cubeObj = await Addressables.InstantiateAsync("Assets/Prefabs/Cube.prefab").Task;
cubeObj.transform.position = Vector3.right * 2;
}
#endregion
}
运行结果如下:
1.3 面板赋值
在代码中声明AssetReference
类型的变量,将上面准备好的Cube拖拽上来进行赋值。若此时拖拽上来的没有未加入可寻址系统的资源,他会自动变成一个可选寻址资源。
准备场景如下:
示例代码如下:
using UnityEngine;
// 引用命名空间
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class LoadManager : MonoBehaviour
{
// 可寻址资源的弱引用
public AssetReference cubeRef;
void Start()
{
// 面板引用
RefLoadCube();
}
#region 面板形式
void RefLoadCube()
{
cubeRef.LoadAssetAsync<GameObject>().Completed += (obj) =>
{
// 加载完成结果
GameObject cubePrefab = obj.Result;
// 实例化
GameObject cubeObj = Instantiate(cubePrefab);
// 修改位置
cubeObj.transform.position = Vector3.zero;
};
}
#endregion
}
运行结果如下:
PS:
如果我们声明的不是AssetReference
类型,而是我们常用的GameObject
类型,那么场景就直接依赖了Cube预制体,打包时Cube预制体就会被打到场景中。现在这里用的是AssetReference
类型,它是一个弱引用,场景并不会真的依赖Cube预制体。
1.4 同步加载
在1.17.4版本之后的可寻址系统,通过在 AsyncOperationHandle
的 WaitForCompletion
方法来实现同步加载。 WaitForCompletion
的作用是会让系统阻拦代码执行,直到资源加载完成。
同步加载GameObject的基本用法,代码如下:
using UnityEngine;
using UnityEngine.AddressableAssets; // 引用命名空间
using UnityEngine.ResourceManagement.AsyncOperations;
public class LoadManager : MonoBehaviour
{
// Asset的弱引用
public AssetReference cubeRef;
void Start()
{
Addressables.InitializeAsync();
// 同步加载
InstantiatePrefab();
}
void InstantiatePrefab()
{
// 实例化加载到的游戏物体
Instantiate(LoadPrefab(), transform);
}
// 强制同步加载GameObject的基本用法
GameObject LoadPrefab()
{
var op = Addressables.LoadAssetAsync<GameObject>("Cube");
GameObject go = op.WaitForCompletion();
return go;
}
}
多数情况下同步加载的性能和异步加载基本一致,但偶尔也会出现更快或更慢的情况。
WaitForCompletion
运算必须在引擎的任务队列中依次完成,如果小型运算的前面有一些大型运算,则只有前面的完成,系统才能完成队列后方的运算。这种情况下会出现加载慢的现象。
二,可寻址系统分标签加载
2.1 场景搭建
创建一个RawImage
和一个Button
摆放位置如下图:
找两张图片将其放入到工程中,并将其设置为可寻址资源,然后把地址都修改为Logo
,最后分别为其创建标签Black
,White
设置如下:
PS:这里创建的地址Logo
和标签Black
,White
是下面代码中要用到的,若有变化,则代码中的值需要对应修改。
2.2 代码示例
创建如下代码并将其挂载到上面创建的RawImage
物体上,并初始化如下:
下面代码,分别使用了AssetLabelReference
面板赋值的形式和寻址
+标签
两种形式进行了分标签加载代码的示例。根据实际情况决定使用哪一种形式即可。
using System.Collections.Generic;
using UnityEngine;
// 引用命名空间
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.UI;
public class LoadByLabelManager : MonoBehaviour
{
// 可寻址系统标签的引用
public AssetLabelReference prefabsLabel;
public Button loadWhiteLogoBtn;
private RawImage _rawImage;
void Start()
{
_rawImage = GetComponent<RawImage>();
loadWhiteLogoBtn.onClick.AddListener(() =>
{
LoadTextureByKeyLabel("Logo", "Black");
});
LoadGameObjectByLabel();
}
/// <summary>
/// 根据标签加载
/// </summary>
void LoadGameObjectByLabel()
{
Addressables.LoadAssetsAsync<Texture2D>(prefabsLabel, (texture) =>
{
// 每加载完成一个,就回调一次。 标签下有几个就会执行几次
Debug.Log("加载完成一个资源: " + texture.name);
_rawImage.texture = texture;
_rawImage.SetNativeSize();
});
}
/// <summary>
/// 根据地址和标签加载
/// </summary>
/// <param name="key">可寻址资源地址</param>
/// <param name="label">资源标签</param>
void LoadTextureByKeyLabel(string key, string label)
{
Addressables.LoadAssetsAsync<Texture2D>(new List<string> {key, label},
null, Addressables.MergeMode.Intersection).Completed += TextureLoaded;
}
void TextureLoaded(AsyncOperationHandle<IList<Texture2D>> texture)
{
_rawImage.texture = texture.Result[0];
_rawImage.SetNativeSize();
}
}
根据寻址和标签加载的代码中Addressables.MergeMode
:用于合并请求结果 --> 可以理解为,可寻址系统依次从key和label加载两组结果,合并结果有四个枚举值:若查询结果分别为:A,B,C 和 B,C,D
- None和UseFirst 都会取第一组结果 --> A,B,C
- Union 取两组结果的并集 --> A,B,C,D
- Intersection 取两组结果的交集 --> B,C
2.3 效果展示
运行效果:
三,代码加载可寻址的解释
代码加载逻辑基本上这几个,写法也都在上面。逻辑和使用AB包加载逻辑基本一致,只是写法略有不同,所以稍微有经验的同学,应该可以很快上手。只需要熟悉熟悉写法即可。
上面代码加载用的都是key加载的,我们也可以使用它的绝对路径加载,比如上面的Cube
,可以这样写:
Addressables.LoadAssetAsync<GameObject>("Cube")
或
Addressables.LoadAssetAsync<GameObject>("Assets/Prefab/Cube.prefab")
使用key
的方式加载的好处在于不管我们之后如何修改路径或者是修改预制体名称,都不会影响到上面代码的加载逻辑,这就是可寻址。只要key
不修改就不用修改代码,若AddressablesGroups
中存在相同的key
,则代码会返回列表中靠上的资源。
若资源不在本地而在服务器上,可寻址系统会帮助我们先下载等待下载完成在执行后面的加载逻辑,所以代码上也不需要有任何的修改。
除了我们常用的资源类型(预制,贴图,音频,配置文件)这些可以使用可寻址代码加载,场景也可以使用可寻址代码加载代码Addressables.LoadSceneAsync("Assets/Scenes/Game.unity");
在后面介绍资源更新的文章中会介绍一个检测更新的脚本,用以游戏开始前下载我们的需要的资源,这样就不会再等到使用时现下载,也就解决了资源过大或者网络稳定的情况下,现下载卡顿的问题。