关注此插件已经有一段时间了,最早在2018就有preview版本了。但直到2019.7.15日 Addressable(1.1.5) 才正式发布第一个正式版。未来它会成为UNITY的内置标准组件,就像之前我介绍过的 IMGUI:UNITY的新UI系统 一样,目前该系统在UNITY2019的UNTY上已经成了标配组件。
从中我也发现了UNITY更新内置功能的规律。 1.放到preview给程序员们试用收集反馈 2.preview迭代后转正式 3.一段时间的正式版后转为内置组建。 那未来要预测UNITY在下版本的功能我们看preview列表中来自 Unity Technologies 的功能就行了.
写本章的主要目的是记录这两天遇到bug的体验。

测试环境

Addressable 2019.8.28 更新到1.1.10版本,也就是本章使用的最新版本。本篇所使用的Unity版本2019.3.f1 如果你也想测试请注意本文的时效性。

Addressable的更新日志

起因

我写了这么一段代码用来测试不用callback 而直接获取实时加载进度与结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private static string loadPath = string.Empty;
IEnumerator loadRes()
{
var operationHandle = Addressables.LoadAssetAsync<GameObject>(loadPath);
Debug.Log(operationHandle.Status.ToString());
if (operationHandle.Status == AsyncOperationStatus.Failed)
{
Debug.Log("load failed");
yield break;
}
while (!operationHandle.IsDone)
{
yield return operationHandle.PercentComplete;
// yield return new WaitForSeconds(.1f);
Debug.LogFormat("loading percent :{0}",operationHandle.PercentComplete);
}
var g = operationHandle.Result;
var g_ins = Instantiate(g);
g_ins.transform.position = Vector3.zero;
yield break;
}
void OnGUI()
{
loadPath = GUILayout.TextField(loadPath);
if (GUILayout.Button("sample load test"))
{
StartCoroutine(loadRes());
}
}

结果我发现我第一次点击test按钮 operationHandle.Status 并不会返回错误信息他返回的是一个None状态,导致后面的代码直接崩溃。
紧接着第二次点击test按钮,变得正常了打印 load failed ,并且 operationHandle.Status 为 failed 。

就这个问题我查了一个晚上,终于给搞明白了 。Addressables 直接加载文件,内部会校验自身是否初始化完毕如果没有初始化 那就执行一次初始化 在进行资源加载作业。
不负责任的猜想是开发这个插件的人觉得 Addressables 同步加载不优雅,直接给 Addressables.Initialize 函数给oabsolute,
改为了Addressables.InitializeSync 接口(非同步的初始化接口)。

现在问题来了,Addressables.InitializeSync 是非同步的,后面的加载函数肯定在 Addressables 还没初始化完毕的情况下调用,出现不可预知的错误。

如何解决:1. 显式初始化 2. 改代码手动添加加载KeyCache

第一种方案直接等待初始化完毕再进行加载处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
IEnumerator loadRes()
{
yield return Addressables.InitializeAsync();
var operationHandle = Addressables.LoadAssetAsync<GameObject>(loadPath);
Debug.Log(operationHandle.Status.ToString());
if (operationHandle.Status == AsyncOperationStatus.Failed)
{
Debug.Log("load failed");
yield break;
}
}

第二种方案这里我说下思路

  1. 找到 InitializeAsync 的 Complete 函数注册完成事件
  2. 在加载资源的入口判断是否初始化完成,没有完成则记录加载资源的Key 然后跳出
  3. InitializeAsync 的 Complete 函数调用后完成事件,根据第二步记录的key重做一次资源加载

参考资料

Getting started with Addressable Assets
The Addressable Asset System 正式版应用(一)
学习Unity新出资源管理系统-Addressable Asset
GitHub Unity-Technologies/Addressables-Sample