UFL5-7——AB包资源管理器的同步加载修改
UFL5-7——AB包资源管理器的同步加载修改
修改同步加载主要面对的问题
在进行同步加载时如果遇到有AB包正在被异步加载应该如何解决?
通过我们之前学习的 潜在问题考虑 得知,在进行异步加载时再重复加载相同AB包是会报错的
即使是同步加载,我们也必须等待异步加载结束,再进行下一步
修改ABMgr中的同步加载相关逻辑
-
注释之前的同步加载代码
1
2
3
4
5
6
7
8//同步加载,不指定类型
//public Object LoadRes(string abName, string resName);
//同步加载,使用type指定类型
//public Object LoadRes(string abName, string resName, System.Type type);
//同步加载,使用泛型指定类型
//public T LoadRes<T>(string abName, string resName) where T : Object; -
在异步加载的基础上进行修改
添加一个
isSync
参数,该参数为true
时,当遇到未加载的AB包或者资源,会全部所有同步API加载,
但如果发现如果存在AB包正在异步加载,则还是和异步方法那样等待加载完毕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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86//根据泛型异步加载
public void LoadResAsync<T>(string abName, string resName, UnityAction<T> callBack, bool isSync = false) where T : Object
{
StartCoroutine(LoadResCoroutine(abName, resName, callBack));
}
private IEnumerator LoadResCoroutine<T>(string abName, string resName, UnityAction<T> callBack, bool isSync = false) where T : Object
{
//加载AB包
LoadMainAB();
//加载主包中的关键配置文件 获取依赖包
string[] strs = manifest.GetAllDependencies(abName);
//加载依赖包
for (int i = 0; i < strs.Length; i++)
{
//判断包是否加载过
if (!abDic.ContainsKey(strs[i]))
{
//调用同步加载AB包的方法
if (isSync)
{
AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);
abDic.Add(strs[i], ab);
}
//异步加载AB包
else
{
//一开始异步加载就记录,如果此时的记录的AB包为空,说明该AB包正在异步加载
abDic.Add(strs[i], null);
AssetBundleCreateRequest req = AssetBundle.LoadFromFileAsync(PathUrl + strs[i]);
yield return req;
//异步加载结束后再替换以前的null,此时记录的AB包不为null,证明加载结束了
abDic[strs[i]] = req.assetBundle;
}
}
//字典存在记录,说明AB包正在异步加载或者加载已经结束了
else
{
//如果字典中记录的消息是null,则证明正在加载中,这时就需要等待加载结束
while (abDic[strs[i]] == null)
{
//只要发现正在加载中,就不停的等待一帧,下一帧再进行判断
yield return 0;
}
}
}
//加载目标包
if (!abDic.ContainsKey(abName))
{
if (isSync)
{
AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + abName);
abDic.Add(abName, ab);
}
else
{
//一开始异步加载就记录,如果此时的记录的AB包为空,说明该AB包正在异步加载
abDic.Add(abName, null);
AssetBundleCreateRequest req = AssetBundle.LoadFromFileAsync(PathUrl + abName);
yield return req;
//异步加载结束后再替换以前的null,此时记录的AB包不为null,证明加载结束了
abDic[abName] = req.assetBundle;
}
}
//字典存在记录,说明AB包正在异步加载或者加载已经结束了
else
{
//如果字典中记录的消息是null,则证明正在加载中,这时就需要等待加载结束
while (abDic[abName] == null)
{
//只要发现正在加载中,就不停的等待一帧,下一帧再进行判断
yield return 0;
}
}
if (isSync)
{
callBack(abDic[abName].LoadAsset<T>(resName));
}
else
{
AssetBundleRequest abr = abDic[abName].LoadAssetAsync<T>(resName);
yield return abr;
//异步加载结束后,通过委托,传递给外部,来使用
callBack(abr.asset as T);
}
}其他的异步加载重载方法,同样添加该参数,加载AB包的逻辑是一致的,加载资源不同的重载调用对应的API
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176//通过名字异步加载资源
public void LoadResAsync(string abName, string resName, UnityAction<Object> callBack, bool isSync = false)
{
StartCoroutine(LoadResCoroutine(abName, resName, callBack, isSync));
}
private IEnumerator LoadResCoroutine(string abName, string resName, UnityAction<Object> callBack, bool isSync = false)
{
//加载AB包
LoadMainAB();
//加载主包中的关键配置文件 获取依赖包
string[] strs = manifest.GetAllDependencies(abName);
//加载依赖包
for (int i = 0; i < strs.Length; i++)
{
//判断包是否加载过
if (!abDic.ContainsKey(strs[i]))
{
//调用同步加载AB包的方法
if (isSync)
{
AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);
abDic.Add(strs[i], ab);
}
//异步加载AB包
else
{
//一开始异步加载就记录,如果此时的记录的AB包为空,说明该AB包正在异步加载
abDic.Add(strs[i], null);
AssetBundleCreateRequest req = AssetBundle.LoadFromFileAsync(PathUrl + strs[i]);
yield return req;
//异步加载结束后再替换以前的null,此时记录的AB包不为null,证明加载结束了
abDic[strs[i]] = req.assetBundle;
}
}
//字典存在记录,说明AB包正在异步加载或者加载已经结束了
else
{
//如果字典中记录的消息是null,则证明正在加载中,这时就需要等待加载结束
while (abDic[strs[i]] == null)
{
//只要发现正在加载中,就不停的等待一帧,下一帧再进行判断
yield return 0;
}
}
}
//加载目标包
if (!abDic.ContainsKey(abName))
{
if (isSync)
{
AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + abName);
abDic.Add(abName, ab);
}
else
{
//一开始异步加载就记录,如果此时的记录的AB包为空,说明该AB包正在异步加载
abDic.Add(abName, null);
AssetBundleCreateRequest req = AssetBundle.LoadFromFileAsync(PathUrl + abName);
yield return req;
//异步加载结束后再替换以前的null,此时记录的AB包不为null,证明加载结束了
abDic[abName] = req.assetBundle;
}
}
//字典存在记录,说明AB包正在异步加载或者加载已经结束了
else
{
//如果字典中记录的消息是null,则证明正在加载中,这时就需要等待加载结束
while (abDic[abName] == null)
{
//只要发现正在加载中,就不停的等待一帧,下一帧再进行判断
yield return 0;
}
}
//同步加载包中资源
if (isSync)
{
//即使是同步加载,也需要使用回调函数传给外部进行使用
callBack(abDic[abName].LoadAsset(resName));
}
//异步加载包中资源
else
{
AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName);
yield return abr;
//异步加载结束后,通过委托,传递给外部,来使用
callBack(abr.asset);
}
}
//根据Type异步加载资源
public void LoadResAsync(string abName, string resName, System.Type type, UnityAction<Object> callBack, bool isSync = false)
{
StartCoroutine(LoadResCoroutine(abName, resName, type, callBack, isSync));
}
private IEnumerator LoadResCoroutine(string abName, string resName, System.Type type, UnityAction<Object> callBack, bool isSync = false)
{
//加载AB包
LoadMainAB();
//加载主包中的关键配置文件 获取依赖包
string[] strs = manifest.GetAllDependencies(abName);
//加载依赖包
for (int i = 0; i < strs.Length; i++)
{
//判断包是否加载过
if (!abDic.ContainsKey(strs[i]))
{
//调用同步加载AB包的方法
if (isSync)
{
AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);
abDic.Add(strs[i], ab);
}
//异步加载AB包
else
{
//一开始异步加载就记录,如果此时的记录的AB包为空,说明该AB包正在异步加载
abDic.Add(strs[i], null);
AssetBundleCreateRequest req = AssetBundle.LoadFromFileAsync(PathUrl + strs[i]);
yield return req;
//异步加载结束后再替换以前的null,此时记录的AB包不为null,证明加载结束了
abDic[strs[i]] = req.assetBundle;
}
}
//字典存在记录,说明AB包正在异步加载或者加载已经结束了
else
{
//如果字典中记录的消息是null,则证明正在加载中,这时就需要等待加载结束
while (abDic[strs[i]] == null)
{
//只要发现正在加载中,就不停的等待一帧,下一帧再进行判断
yield return 0;
}
}
}
//加载目标包
if (!abDic.ContainsKey(abName))
{
if (isSync)
{
AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + abName);
abDic.Add(abName, ab);
}
else
{
//一开始异步加载就记录,如果此时的记录的AB包为空,说明该AB包正在异步加载
abDic.Add(abName, null);
AssetBundleCreateRequest req = AssetBundle.LoadFromFileAsync(PathUrl + abName);
yield return req;
//异步加载结束后再替换以前的null,此时记录的AB包不为null,证明加载结束了
abDic[abName] = req.assetBundle;
}
}
//字典存在记录,说明AB包正在异步加载或者加载已经结束了
else
{
//如果字典中记录的消息是null,则证明正在加载中,这时就需要等待加载结束
while (abDic[abName] == null)
{
//只要发现正在加载中,就不停的等待一帧,下一帧再进行判断
yield return 0;
}
}
if (isSync)
{
callBack(abDic[abName].LoadAsset(resName, type));
}
else
{
AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName, type);
yield return abr;
//异步加载结束后,通过委托,传递给外部,来使用
callBack(abr.asset);
}
}
使用示例
先同步加载,再两次异步加载,再同步加载同一包下的同一资源
1 | public class Main : MonoBehaviour |
输出:
抛出改进方向
我们已经完成了对ABMgr的修改,主要实现的就是彻底的异步化,对于大家来说还有可以修改的内容,比如:
- 完善卸载资源逻辑
- 添加AB包或资源预加载功能
- 考虑AB包加载失败的情况
具体代码
1 | using System.Collections; |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 文KRIFE齐的博客!