框架之资源加载

本篇我们实现unity里的加载模块,他的主要功能是,业务传入资源名字和资源类型,加载模块加载到对应的资源后返回给业务,业务不需要关心该资源是从本地加载还是从AssetBundle里加载。

加载模块分两部分1.各资源的加载器,例如ab包加载器、Asset加载器、网络下载。2.各加载器的管理类,提供给业务的接口都在这里

需要支持的能力

1.能切换不同加载模式 开发阶段编辑器运行直接加载资源无需打ab包,测试或正式发布阶段通过ab包加载资源

2.缓存机制 能定时清理长时间未使用的资源内存

3.既有同步加载 也有异步加载

复杂点:

1.根据业务传入的资源名字,获取到editor路径、ab包名字。需要事先根据资源名字保存资源的路径、ab包路径配置。

2.ab包的引用计数维护:加载时ReferencedCount+1,卸载时ReferencedCount-1。

两种引用:AB包之间的相互依赖,ab包加载时,依赖包引用计数加1,ab包卸载时,依赖包引用减1。2.资源引用,例如使用AssetBundle.LoadAsset加载资源时,该ab包引用计数加一,引用对象被删除时,引用计数减1.

问题是如何确保被删除的引用对象引用计数能正确减少。

有两种方式:

  1.纯引用计数。ab包依赖和asset引用都使用引用计数。asset引用类型大概以下几种

  1-1.预制体,额外封装一层,所有预制体的生成和销毁都由一个管理类统一管理。

例如封装一个ResourceItem类,所有预制体的生成和销毁都必须走这个类的的Create和Dispose类,在ctor方法里加载ab包、实例化预制体,在Dispose方法里Distory对象、卸载ab包(这里的加、卸载只是引用计数加1、减1)。需要业务手动的释放调用Dispose释放对象。

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
-- 资源
-- 所有非UI的预制加载
local ResourceItem = class("ResourceItem", ObjectBase)

-- 静态创建ResourceItem接口
-- path Data目录以下,预制的路径
function ResourceItem.Create(target, filepath, parent, onLoaded, async)
local abpath = PubFunc.GetAbNameOfPath(filepath)
local name = PubFunc.GetNameFromPath(filepath)
local item = ResourceItem.new(filepath, abpath, name, parent, onLoaded, async)
target:AddSubItem(item)
return item
end

function ResourceItem.CreateUIItem(target, abpath, name, parent, onLoaded, async)
local filepath = abpath
if string.find(abpath, name)==nil then
filepath = abpath..name
end

local item = ResourceItem.new(filepath, abpath, name, parent, onLoaded, async)
target:AddSubItem(item)
return item
end

function ResourceItem:ctor(filepath, abpath, name, parent, onLoaded, async)
ResourceItem.super.ctor(self)async = async and true or false

self._filepath = filepath
self._path = abpath -- 文件路径
self._name = name
self._parent = parent
self._onLoaded = onLoaded -- 加载完回调

self.gameObject = nil --外部可直接获取
self.transform = nil

self._loadKey = me.modules.resource:CreateAsyn(abpath, name, handler(self, self.OnLoadComplete), async)
end-- 清理
function ResourceItem:Dispose()
if self.gameObject then
-- 销毁
me.modules.resource:Delete(self.gameObject)
self.gameObject = nil
self.transform = nil
elseif self._loadKey then
-- 取消加载
me.modules.resource:CancelLoad(self._loadKey)
self._loadKey = nil
end

ResourceItem.super.Dispose(self)
end-- 是否已加载
function ResourceItem:IsLoaded()
return self.gameObject ~= nil
end

-- 加载完成回调
function ResourceItem:OnLoadComplete(go)
self._loadKey = nil
local trans = nil

if go then
trans = go.transform
if self._parent then
go:SetParent(self._parent)
end
trans:SetLocalPositionZero()
trans:SetLocalScaleOne()
else
printError("加载RedourceItem失败,path:", self._path)
end
self.gameObject = go
self.transform = trans

-- 回调给外部
if self._onLoaded then
self._onLoaded(self)
end
end

return ResourceItem

你也可以在每个实例化的GameObject上挂在一个脚本,并在该脚本的Destory方法里卸载ab包的引用  

1-2.场景类,这个比较简单,场景管理类肯定会记录当前的场景信息,在加载新场景时,先卸载当前的ab包就可以了。

1-3.sprite类,sprite是给image使用的,那么我们可以扩展一下Image的类。例如业务传入图片的名字,ImageEx类根据名字到LoadModule加载对应的ab及sprite并记录当前的sprite名字,当业务下次设置图片或Image对象被Destory时,根据保存的sprite名字卸载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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Debugger = LuaInterface.Debugger;

/// <summary>
/// image扩展,提供通过图片名字加载图片、从ab包、url、高清资源下载、emoji加载图片接口
/// TP形Image.
/// </summary>
public class ImageEx : Image
{
private string m_SpriteName = "";
public string SpriteName
{
get
{
return m_SpriteName;
}
set
{
m_SpriteName = value;
}
}protected override void OnDestroy()
{
// 销毁的时候要卸载一下ab
UnloadSprite();
StopCurrLoadingUrl();
}

private void UnloadSprite()
{
if (!string.IsNullOrEmpty(SpriteName) && sprite != null)
{
SpriteModule.Instance.UnloadSpriteByName(SpriteName);
SpriteName = null;
this.sprite = null;
}
}public void SetSpriteName(string name)
{
if (sprite != null && SpriteName == name)
{
return;
}

UnloadSprite();

if(string.IsNullOrEmpty(name))
{
this.sprite = null;
return;
}

SpriteName = name;
SpriteModule.Instance.LoadSpriteByName(name, onLoadedSprite);
}private void onLoadedSprite(object obj)
{
Sprite sp = obj as Sprite;
this.sprite = sp;
if (sp == null)
{
Debugger.LogError("Load sprite null, name:{0}", m_SpriteUrl);
}
else
{
if(!string.IsNullOrEmpty(m_strHdResName))
{
sprite.name = m_strHdResName;
}
}
}
}

  1-4.shader类:全局就一个ab包,常驻内存就好了

  1-5.音乐类:PlayMusic加载、StopMusic卸载就好了

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
// 背景音乐
// fromResources 是否从Resources文件夹下加载
public void PlayMusic(string name, bool fromResources = false)
{
if (string.IsNullOrEmpty(name)) return;

if (fromResources)
{
if(MusicMute)
{
return;
}
AudioClip clip = Resources.Load<AudioClip>(name);
if(clip != null)
{
m_musicSource.enabled = true;
m_musicSource.clip = clip;
m_musicSource.loop = true;
m_musicSource.Play();
}
}
else
{
string strBundleName = "sound/music/" +name;
LoadModule.Instance.LoadAssetFromBundle(strBundleName, name, typeof(AudioClip), (data) => {
m_musicSource.enabled = true;
m_musicSource.clip = data as AudioClip;
m_musicSource.loop = true;
m_musicSource.Play();
});
}
}

/// <summary>
/// 停止音乐并清理
/// </summary>
public void StopMusic()
{
AudioClip m_musicClip = m_musicSource.clip;
if (m_musicClip)
{
m_musicSource.Stop();
m_musicSource.clip = null;

string currentMusicName = m_musicClip.name;
AssetBundleCache assetBundleCache = ABCachePool.Instance.GetABCacheByName(string.Format("sound_music_{0}.unity3d", currentMusicName));
if (assetBundleCache != null)
{
assetBundleCache.ReferencedCount = 1;
LoadModule.Instance.UnloadAssetBundle(string.Format("sound/music/{0}", currentMusicName), true);
}
}
}

  2.引用计数+弱引用。ab包依赖使用引用计数,asset引用使用弱引用,业务在加载asset时需要传入引用的对象(实例化就不用了,可以把实例化出来的GameObject当作引用对象),通过判断对象是否为空来判断引用关系。

  强引用:我们实例化一个对象,直接引用了这个对象就是强引用。在这个对象被强引用的时,GC无法回收这个对象。只有当该对象所有的强引用都失去的时候,GC才会回收该对象。

  弱引用:弱引用可以保持对对象的引用,同时允许GC在必要时释放对象,回收内存。这边一定要用弱引用,不然会影响对象的回收。

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
protected List<System.WeakReference> mReferenceOwnerList;
/// <summary>
/// 为AB添加指定owner的引用
/// 所有owner都销毁则ab引用计数归零可回收
/// </summary>
/// <param name="owner"></param>
protected void retainOwner(UnityEngine.Object owner)
{
if (owner == null)
{
ResourceLogger.logErr(string.Format("引用对象不能为空!无法为资源:{0}添加引用!", AssetBundleName));
return;
}

foreach (var referenceowner in mReferenceOwnerList)
{
if (owner.Equals(referenceowner))
{
return;
}
}

System.WeakReference wr = new System.WeakReference(owner);
mReferenceOwnerList.Add(wr);
}
/// <summary>
/// 获取AB有效的引用对象计数
/// </summary>
/// <returns></returns>
protected int updateOwnerReference()
{
for (int i = 0; i < mReferenceOwnerList.Count; i++)
{
UnityEngine.Object o = (UnityEngine.Object)mReferenceOwnerList[i].Target;
if (!o)
{
mReferenceOwnerList.RemoveAt(i);
i--;
}
}
return mReferenceOwnerList.Count;
}

第一种方式需要业务手动Dispose无用的对象,当然这是个好习惯,需要框架严格注意引用对象的管理。第二种需要业务在引用时传入引用的对象,需要额外的参数。

一个加载模块大致结构如下:

加载模块结构如上图,load为加载器,ResManager为提供给业务调用的接口,LoadModule为不使用ab包的加载接口,ABLoadModule为使用ab包的加载接口,这两个module对用户封闭。

AssetLoader:在editor模式下加载资源。AssetBundleLoader :ab包加载器,负责从内存加载AssetBundle。BundleAssetLoader :负责从指定的ab包加载资源。AssetBundleCache:缓存的ab包。

一、加载器实现

上篇我们有说到,unity有四种加载方式

1.AssetDatabase:在编辑器内加载卸载资源,并不能在游戏发布时使用,它只能在编辑器内使用。但是,它加载速度快,使用简单。

2.Resources:因为使用Resources文件夹无法热更,本片篇就不实现此途径了。

3.AssetBundle:支持热更,但是每次资源变化都得重新打ab包(奇慢),所以适合发布模式,但开发模式千万别用。

4.UnityWebRequest:从网络端下载

1.所有的加载器都继承自一个接口:Loader,该类定义了当前的加载类型、初始化、回收的重置方法、加载方法、加载进度回调等

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
public class Loader
{
#region Define
public enum LoaderType
{
STREAM, // 流(原则上可以是任何文件,包括远程服务器上的)
ASSET, // Asset目录下的资源
BUNDLE, // AssetBundle
BUNDLEASSET, // AssetBundle中的资源
SCENE, // 场景
Texture, // 图片
}

public enum LoaderState
{
NONE, //
LOADING, // 加载中
FINISHED, // 完成
}

public delegate void ProgressHandle(Loader loader, float rate);
public delegate void LoadedHandle(Loader loader, object data);
#endregion

protected Loader(LoaderType type)
{
m_type = type;
}

protected LoaderType m_type; // 加载器类型
protected LoaderState m_state; // 加载状态
protected string m_path; // 路径
protected bool m_async; // 是否异步

protected ProgressHandle m_onProgress; // 加载进度
protected LoadedCallback m_onloaded; // 加载完成回调通知

protected System.Diagnostics.Stopwatch m_watch = new System.Diagnostics.Stopwatch ();//加载时间统计

public LoaderType Type { get { return m_type; } }
public string Path { get { return m_path; } }
public bool IsFinish { get { return m_state == LoaderState.FINISHED; } }
public bool IsAsync { get { return m_async; } }

  //主要用于ab包的判断,因为ab包需要等待依赖包的加载
public virtual bool IsPrepareToLoad()
{
return true;
}

//初始化参数
public virtual void Init(string path, LoadedCallback onloaded, bool async = true)
{
m_state = LoaderState.NONE;
m_path = path;
m_async = async;
m_onloaded = onloaded;
}

public virtual void Reset()
{
m_path = "";
m_async = true;
m_onloaded = null;
m_state = LoaderState.NONE;
m_onProgress = null;
}

public virtual void Load()
{
m_watch.Reset ();
m_watch.Start ();
m_state = LoaderState.LOADING;
OnLoadProgress(0f);
}

public virtual void Stop()
{
Reset();
}

public virtual void Update(float dt)
{

}

protected virtual void OnLoadProgress(float rate)
{
if (m_onProgress != null)
{
m_onProgress(this, rate);
}
}

protected virtual void OnLoadCompleted(object data)
{
m_state = LoaderState.FINISHED;

try
{
if (m_onloaded!= null)
{
m_onloaded (data);
}
}
catch(System.Exception e)
{
LuaInterface.Debugger.LogError(e.Message);
}

OnLoadProgress(1f);
}
}

2.editor模式下的加载,直接使用AssetDatabase加载。

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
public class AssetLoader : Loader
{
Object m_data = null;
System.Type m_assetType = null; //资源类型

public AssetLoader()
: base(Loader.LoaderType.ASSET)
{

}

public void Init (string path, System.Type type, LoadedCallback onLoaded, bool async = true)
{
base.Init (path, onLoaded, async);
m_assetType = type;
}

public override void Load()
{
base.Load();

#if UNITY_EDITOR
if (m_assetType == null)
{
m_assetType = typeof(Object);
}

m_data = UnityEditor.AssetDatabase.LoadAssetAtPath(m_path, m_assetType);
if (!m_async)
{
OnLoadCompleted(m_data);
}
#else
if(!m_async)
{
OnLoadCompleted(null);
}
#endif
}

public override void Update(float dt)
{
if (m_state == LoaderState.LOADING)
{
OnLoadCompleted(m_data);
m_data = null;
}
}
}

3.ab包的缓存可以参考我之前的文章:ab包的缓存

ab加载如下:当且仅当IsPrepareToLoad判断通过(即所有依赖包都加载完成)才能调用load方法,开始ab包的加载。InitDependencies方法用于初始化当前ab包的依赖项

load加载分两种,第一种是从扩展包加载,第二种是从本地加载。

ab包的加载无非就是同步和异步加载的区别,ab包的卸载也只需要调用Unload方法就好了。唯一需要注意的是,记载asset前必须保证ab的依赖包都加载完成了。

AssetBundleCache:缓存类,用于缓存ab包,提供从ab包加载asset的方法并缓存a包

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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
public class AssetBundleCache
{
private string m_name; // AssetBundle name
private int m_referencedCount; // 引用计数
private float m_unloadTime; // 释放时间

private HashSet<string> m_setAllAssetNames = null;//ab包包含的所有asset的名字,用于判断指定asset是否存在于ab中
private Dictionary<string, AssetBundleRequest> m_dicAsync = new Dictionary<string, AssetBundleRequest>();//正在异步加载的asset
private Dictionary<string, Object> m_dicAssetCache = null;//已经加载完的asset

public AssetBundleCache(string name, AssetBundle ab, int refCount)
{
m_name = name;
Bundle = ab;
ReferencedCount = refCount;
}

// AssetBundle
public AssetBundle Bundle
{
get;
private set;
}

// 是否常驻,通用资源的ab包不卸载
public bool Persistent
{
get;
set;
}

public string BundleName
{
get
{
return m_name;
}
}

// 引用计数
public int ReferencedCount
{
get
{
return m_referencedCount;
}

set
{
m_referencedCount = value;
if (m_referencedCount <= 0)
{
m_unloadTime = Time.realtimeSinceStartup;
}
else
{
m_unloadTime = 0;
}
if (m_referencedCount < 0)
{
Debug.LogWarningFormat("AssetBundleCache reference count < 0, name:{0}, referencecount:{1}", m_name, m_referencedCount);
}
}
}

// 是否可以删除
public bool IsCanRemove
{
get
{
// 常驻资源
if (Persistent) return false;

// 非常驻,并且引用计数为0
if (!Persistent && ReferencedCount <= 0)
{
return true;
}

return false;
}
}

// 缓存时间到
public bool IsTimeOut
{
get
{
return Time.realtimeSinceStartup - m_unloadTime >= Config.Instance.AssetCacheTime;
}
}

// 资源是否正在异步加载中
public bool IsAssetLoading(string name)
{
return m_dicAsync.ContainsKey(name);
}

//判断AB是否包含某个资源
public bool IsExistAsset(string strAssetName)
{
if (m_setAllAssetNames != null && m_setAllAssetNames.Contains(strAssetName))
{
return true;
}
return false;
}

// 获取缓存中的资源
public Object GetCacheAsset(string name)
{
if (m_dicAssetCache != null)
{
Object rst = null;
if (m_dicAssetCache.TryGetValue(name, out rst))
{
return rst;
}
}
return null;
}

// 资源加载完 要缓存一下
public void OnLoadedAsset(string name, Object asset)
{
m_unloadTime = Time.realtimeSinceStartup;

if (m_dicAsync.ContainsKey(name))
{
m_dicAsync.Remove(name);
}

// 常驻ab加载进来的资源 用真实引用 不用弱引用
if (m_dicAssetCache == null)
{
m_dicAssetCache = new Dictionary<string, Object>();
}
if (m_dicAssetCache.ContainsKey(name))
{
Debug.LogWarningFormat("警报! 缓存已经存在了还重新加载:{0}", name);
}
m_dicAssetCache[name] = asset;
return;
}

//异步加载资源,需要添加到m_dicAsync里,防止重复加载
public AssetBundleRequest LoadAssetAsync(string name, System.Type type)
{
if (Bundle == null)
{
Debug.LogWarningFormat("AssetBundle:{0} is null, load asset async:{1},type:{2}, fail!!", m_name, name, type.ToString());
return null;
}
AssetBundleRequest opt;
m_dicAsync.TryGetValue(name, out opt);
if (opt == null)
{
opt = Bundle.LoadAssetAsync(name, type);
m_dicAsync.Add(name, opt);
}
return opt;
}

//加载资源
public Object LoadAsset(string name, System.Type type)
{
if (Bundle == null)
{
Debug.LogWarningFormat("AssetBundle:{0} is null, load asset:{1},type:{2}, fail!!", m_name, name, type.ToString());
return null;
}

Object asset = Bundle.LoadAsset(name, type);
if (asset == null)
{
Debug.LogWarningFormat("AssetBuncleCache.LoadAsset, asset not exist:{0}, {1}", m_name, name);
}
else
{
OnLoadedAsset(name, asset);
}

return asset;
}

//加载所有资源
public UnityEngine.Object[] LoadAllAssets(bool bCache = true)
{
UnityEngine.Object[] allObjs = Bundle.LoadAllAssets();
if (bCache)
{
for (int i = 0; i < allObjs.Length; i++)
{
Object obj = allObjs[i];
OnLoadedAsset(obj.name, obj);
}
}

return allObjs;
}

//异步加载所有资源 只用作预加载使用
public AssetBundleRequest LoadAllAssetsAsync()
{
if (Bundle == null)
{
return null;
}
return Bundle.LoadAllAssetsAsync();
}

public bool LoadAllAssetNames()
{
if (Bundle == null)
{
return false;
}
string[] arrNames = Bundle.GetAllAssetNames();
if (arrNames.Length == 0)
{
return false;
}
if (m_setAllAssetNames == null)
{
m_setAllAssetNames = new HashSet<string>();
}

for (int i = 0; i < arrNames.Length; i++)
{
string strName = System.IO.Path.GetFileNameWithoutExtension(arrNames[i]);
m_setAllAssetNames.Add(strName);
}
return true;
}

//卸载ab包
public void Unload()
{
if (m_dicAsync.Count > 0)
{
Debug.LogWarningFormat("[仅提醒]该Bundle还有资源在加载中,暂时不卸载:{0}", m_name);
return;
}

if (Bundle != null)
{

Bundle.Unload(false);
Bundle = null;
}

if (m_setAllAssetNames != null)
{
m_setAllAssetNames.Clear();
}

if (m_dicAssetCache != null)
{
m_dicAssetCache.Clear();
}
}
}

ABCachePool:负责管理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
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
public class ABCachePool
{
#region Instance
private static ABCachePool m_Instance;
public static ABCachePool Instance
{
get { return m_Instance ?? (m_Instance = new ABCachePool()); }
}
#endregion
Dictionary<string, AssetBundleCache> m_AssetBundleCaches = new Dictionary<string, AssetBundleCache>(); // 缓存队列
HashSet<string> m_persistentABs = new HashSet<string>();

public Dictionary<string, AssetBundleCache> AssetBundleCaches
{
get
{
return m_AssetBundleCaches;
}
}

public void ClearAllCache()
{
foreach (KeyValuePair<string, AssetBundleCache> keyval in m_AssetBundleCaches)
{
keyval.Value.Unload();
}
m_AssetBundleCaches.Clear();
}

public bool IsExistCache(string abName)
{
return m_AssetBundleCaches.ContainsKey(abName);
}

// 引用这个bundle
public AssetBundleCache ReferenceCacheByName(string abName)
{
AssetBundleCache cache = null;
m_AssetBundleCaches.TryGetValue(abName, out cache);
if (cache != null)
{
++cache.ReferencedCount;
}
return cache;
}

// 获取ABCache 不增加引用
public AssetBundleCache GetABCacheByName(string abName)
{
AssetBundleCache cache = null;
m_AssetBundleCaches.TryGetValue(abName, out cache);
return cache;
}

public AssetBundleCache AddCache(string abName, AssetBundle bundle, int refCount)
{
if (m_AssetBundleCaches.ContainsKey(abName))
{
Debug.LogWarningFormat("AssetBundleCache already contains key:{0}, it will be cover by new value.", abName);
}

AssetBundleCache cache = new AssetBundleCache(abName, bundle, refCount);
m_AssetBundleCaches[abName] = cache;

if (m_persistentABs.Contains(abName))
{
cache.Persistent = true;
}

return cache;
}

// immediate 只有场景是立刻卸载
public AssetBundleCache UnReferenceCache(string abName, bool immediate = false)
{
AssetBundleCache cache = null;
if (!m_AssetBundleCaches.TryGetValue(abName, out cache))
{
return null;
}

if (cache.Persistent)
{
return null;
}

--cache.ReferencedCount;

if (immediate && cache.IsCanRemove)
{
RemoveCache(abName);
}

return cache;
}

public void RemoveCache(string abName)
{
AssetBundleCache cache = m_AssetBundleCaches[abName];
cache.Unload();
m_AssetBundleCaches.Remove(abName);
}

private List<string> m_lstRm = new List<string>();
// 清除无引用的AssetBundle缓存
public void ClearNoneRefCache(bool mustTimeout)
{
foreach (KeyValuePair<string, AssetBundleCache> keyval in m_AssetBundleCaches)
{
AssetBundleCache item = keyval.Value;
// 只清除引用计数为0的
if (item.IsCanRemove && (!mustTimeout || item.IsTimeOut))
{
m_lstRm.Add(keyval.Key);
}
}

for (int i = 0; i < m_lstRm.Count; i++)
{
RemoveCache(m_lstRm[i]);
}
m_lstRm.Clear();
}

/// <summary>
/// 常驻ab包设置
/// </summary>
/// <param name="arrAB"></param>
public void SetPersistentABs(string[] arrAB)
{
m_persistentABs.Clear();
for (int i = 0; i < arrAB.Length; i++)
{
string strAB = arrAB[i];//FileHelper.GenBundlePath(arrAB[i]);
m_persistentABs.Add(strAB);

AssetBundleCache abCache;
m_AssetBundleCaches.TryGetValue(strAB, out abCache);
if (abCache != null)
{
abCache.Persistent = true;
}
}
}
}

BundleLoader:用于加载AssetBundle

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
public class BundleLoader : Loader
{
AssetBundleCreateRequest m_abRequest = null;

private int m_iRefCount; // 当前等待此加载的引用计数
private List<string> m_lstDepAbNames = new List<string>(); //依赖的未加载Bundle名字列表

private LoadedCallback m_onABLoaded;

private string m_strBundleName; // 相对路径
public string BundleName { get { return m_strBundleName; } }

System.Diagnostics.Stopwatch m_saveAbWatcher = new System.Diagnostics.Stopwatch();
public BundleLoader()
: base(Loader.LoaderType.BUNDLE)
{

}

public void AddLoadedCallback(LoadedCallback onloaded)
{
m_onABLoaded += onloaded;
m_iRefCount += 1;
}

public void AddReferenced()
{
m_iRefCount += 1;
}

public override void Reset()
{
base.Reset();

m_iRefCount = 0;
m_lstDepAbNames.Clear();
m_strBundleName = "";

m_abRequest = null;
m_onABLoaded = null;
m_lstDepAbNames.Clear();
}

// 判断是否所有依赖都已经加载
public override bool IsPrepareToLoad()
{
for (int i = m_lstDepAbNames.Count - 1; i >= 0; i--)
{
string strABName = m_lstDepAbNames[i];
if (ABCachePool.Instance.IsExistCache(strABName))
{
m_lstDepAbNames.RemoveAt(i);
}
else
{
break;
}
}
return m_lstDepAbNames.Count == 0;
}

public void Init(string path, string strName, string[] dependencies, LoadedCallback onloaded, bool async = true)
{
// Bundle 比较特殊 不使用父类的回调
base.Init(path, null, async);

m_iRefCount = 1;
m_strBundleName = strName;
m_onABLoaded = onloaded;
InitDependencies(dependencies);
}

private void InitDependencies(string[] dependencies)
{
if (dependencies == null || dependencies.Length == 0)
return;
for (int i = 0; i < dependencies.Length; i++)
{
string strName = dependencies[i];
if (!ABCachePool.Instance.IsExistCache(strName))
{
m_lstDepAbNames.Add(strName);
}
}
}

public override void Load()
{
base.Load();

if (m_async)
{
string path = FileHelper.GetAPKPath(m_path);//根据ab包的名字索引ab包的存储路劲
m_abRequest = AssetBundle.LoadFromFileAsync(path);
}
else
{
AssetBundle ab = null;
try
{
// 同步使用AssetBundle.LoadFromFile加载,速度最快
if (ab == null)
{
string path = FileHelper.GetAPKPath(m_path);//根据ab包的名字索引ab包的存储路劲
ab = AssetBundle.LoadFromFile(path);
}
}
catch (System.Exception e)
{
Debug.LogError(e.Message);
}
finally
{
OnLoaded(ab);
}
}
}

public override void Update(float dt)
{
if (m_state == LoaderState.LOADING)
{
if (m_abRequest != null)
{
if (m_abRequest.isDone)
{
OnLoaded(m_abRequest.assetBundle);
}
else
{
DoProgress(m_abRequest.progress);
}
}
}
}

void DoProgress(float rate)
{
}

void OnLoaded(AssetBundle ab)
{
AssetBundleCache cache = ABCachePool.Instance.AddCache(m_strBundleName, ab, m_iRefCount);
OnLoadCompleted(ab);

if (m_onABLoaded != null)
{
m_onABLoaded(cache);
}
}
}

二、缓存池,缓存加载器

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
public class LoaderPool
{
#region Instance
private static LoaderPool m_Instance;
public static LoaderPool Instance
{
get { return m_Instance ?? (m_Instance = new LoaderPool()); }
}
#endregion

List<Loader> m_bundleLoaderPool = new List<Loader>(); // BundleLoader的缓存
List<Loader> m_assetLoaderPool = new List<Loader>(); // BundleAssetLoader的缓存

public T GetLoader<T>() where T : Loader, new()
{
System.Type type = typeof(T);
T loader = null;
if (type == typeof(BundleLoader))
{
if (m_bundleLoaderPool.Count > 0)
{
loader = (T)m_bundleLoaderPool[0];
m_bundleLoaderPool.RemoveAt(0);
return loader;
}
}
else if (type == typeof(BundleAssetLoader))
{
if (m_assetLoaderPool.Count > 0)
{
loader = (T)m_assetLoaderPool[0];
m_assetLoaderPool.RemoveAt(0);
return loader;
}
}

loader = new T();

return loader;
}

public void RecycleLoader(Loader loader)
{

if (loader.GetType() == typeof(BundleLoader))
{
loader.Reset();
m_bundleLoaderPool.Add(loader);
}
else if (loader.GetType() == typeof(BundleAssetLoader))
{
loader.Reset();
m_assetLoaderPool.Add(loader);
}
else
{
loader.Reset();
}
}
}

三、加载管理器LoadModule

1.ResManager :提供给业务的接口

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
public class ResManager
{
#region Instance
private static ResManager m_Instance;
public static ResManager Instance
{
get
{
return m_Instance ?? (m_Instance = new ResManager());
}
}
#endregion
public int SyncCount = 6; // 同步加载并发数

private bool m_isUseAssetBundle;
private ILoadModule m_loadModule;

public void Init(bool isUseAssetBundle)
{
m_isUseAssetBundle = isUseAssetBundle;
if (isUseAssetBundle)
{
m_loadModule = new ABLoadModule();
}
else
{
m_loadModule = new LoadModule();
}

m_loadModule.Init(SyncCount);
}

public void UnInit()
{
m_loadModule.UnInit();
}

public void Update(float dt)
{
m_loadModule.Update(dt);
}

public void Clear()
{
ABCachePool.Instance.ClearNoneRefCache(false);

Resources.UnloadUnusedAssets();
}

public void LoadPrefab(string strPath, string name, LoadedCallback onLoaded, bool async = true)
{
m_loadModule.LoadPrefab(strPath, name, onLoaded, async);
}

public void LoadMusic(string name, LoadedCallback onLoaded, bool async = true)
{
m_loadModule.LoadMusic(name, onLoaded, async);
}

public void LoadFont(string name, LoadedCallback onLoaded, bool async = true, bool inBundle = false)
{
m_loadModule.LoadFont(name, onLoaded, async, inBundle);
}

public void LoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded)
{
m_loadModule.LoadScene(name, scenePath, isAdditive, onLoaded);
}
}

2.两种加载器的接口类ILoadModule

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface ILoadModule
{
void Init(int syncCout);

void UnInit();

void Update(float dt);

void LoadPrefab(string strPath, string name, LoadedCallback onLoaded, bool async = true);

void LoadMusic(string name, LoadedCallback onLoaded, bool async = true);

void LoadFont(string name, LoadedCallback onLoaded, bool async = true, bool inBundle = false);

void LoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded);
}

3.editor模式下的加载器

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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/// <summary>
/// 不使用ab加载资源(Editor模式下)
/// </summary>
public class LoadModule : ILoadModule
{
public int SyncCount; // 同步加载并发数
public HashSet<string> m_loadedBundleNames = new HashSet<string>();
// 加载队列
List<Loader> m_loadings = new List<Loader>();//正在加载
List<Loader> m_loaderQueue = new List<Loader>();//等待加载

float m_lastClear = 0; // 上一次清除时间

public void Init(int syncCout)
{
SyncCount = syncCout;
}

public void UnInit()
{
for (int i = 0; i < m_loadings.Count; i++)
{
m_loadings[i].Stop();
}

m_loadings.Clear();
m_loaderQueue.Clear();
}

private List<int> m_lstRmTemp = new List<int>();
public void Update(float dt)
{
for (int i = m_loadings.Count - 1; i >= 0; i--)
{
Loader loader = m_loadings[i];
loader.Update(dt);
if (loader.IsFinish)
{
m_loadings.RemoveAt(i);
LoaderPool.Instance.RecycleLoader(loader);
}
}

int remain = Mathf.Min(SyncCount - m_loadings.Count, m_loaderQueue.Count);
for (int i = 0; i < m_loaderQueue.Count; i++)
{
Loader loader = m_loaderQueue[i];
m_loadings.Add(loader);
loader.Load();
loader.Update(dt);
m_lstRmTemp.Add(i);
if (m_lstRmTemp.Count >= remain)
{
break;
}
}

if (m_lstRmTemp.Count > 0)
{
for (int i = m_lstRmTemp.Count - 1; i >= 0; i--)
{
m_loaderQueue.RemoveAt(m_lstRmTemp[i]);
}
m_lstRmTemp.Clear();
}
}

public void Clear()
{
Resources.UnloadUnusedAssets();
}

public void LoadPrefab(string strPath, string name, LoadedCallback onLoaded, bool async = true)
{
LoadAssetByPath(strPath, name, typeof(GameObject), onLoaded, async);
}

public void LoadMusic(string name, LoadedCallback onLoaded, bool async = true)
{
string path = string.Format("music/{0}", name);
LoadAssetByPath(path, name, typeof(AudioClip), onLoaded, async);
}

public void LoadFont(string name, LoadedCallback onLoaded, bool async = true, bool inBundle = false)
{
string path = string.Format("ui/font/{0}", name);
LoadAssetByPath(path, name, typeof(Font), onLoaded, async);
}

#region LoadScene
public void LoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded)
{
var activeSceneName = SceneManager.GetActiveScene().name;
// 如果当前场景是要加载的场景 直接返回
if (activeSceneName == name)
{
if (onLoaded != null)
{
onLoaded(null);
}
return;
}

if (isAdditive)
{
__LoadScene(name, scenePath, isAdditive, onLoaded);
}
else //大场景先加载idle过渡
{
__LoadScene(name, scenePath, isAdditive, onLoaded);
}
}

private void __LoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded, bool async = true)
{
SceneLoader sLoader = LoaderPool.Instance.GetLoader<SceneLoader>();
sLoader.Init(name, scenePath, isAdditive, onLoaded, async);
StartLoad(sLoader, true);
}
#endregion

//从Bundle中加载资源
public void LoadAssetByPath(string path, string name, System.Type type, LoadedCallback onLoaded, bool async = true)
{
path = "Assets/Data/" + path;
if (Directory.Exists(path))
{
path += "/" + name;
}

string suffix = GetSuffixOfAsset(type);
string fullPath = string.Format("{0}.{1}", path, suffix);
LoadAsset(fullPath, onLoaded, type, false);
}

// 加载资源(Assets目录下,带后缀)
public void LoadAsset(string path, LoadedCallback onLoaded, System.Type type = null, bool async = true)
{
if (!File.Exists(path))
{
Debug.LogErrorFormat("Load Asset, Path:[{0}] not exist! ", path);
if (onLoaded != null)
{
onLoaded(null);
}
return;
}

AssetLoader aLoader = LoaderPool.Instance.GetLoader<AssetLoader>();
aLoader.Init(path, type, onLoaded, async);
StartLoad(aLoader, async);
}

void StartLoad(Loader loader, bool async, bool toHead = false)
{
// 异步加载或者加载还未准备好,则当做异步处理,外部控制是否加入队列开头
// 同步加载,并且已经具备加载条件,则直接调用Load进行加载
if (async || !loader.IsPrepareToLoad())
{
if (toHead)
{
m_loaderQueue.Insert(0, loader);
}
else
{
m_loaderQueue.Add(loader);
}
}
else
{
m_loadings.Add(loader);
loader.Load();
}
}

// 卸载关卡场景
public void UnloadLevelScene(string sceneName, bool immediate)
{
SceneManager.UnloadSceneAsync(sceneName);
}

private void CallFunc_LoadedBack(LoadedCallback callback, object data)
{
if (callback != null)
{
callback(data);
}
}

private string GetSuffixOfAsset(System.Type type)
{
if (type == typeof(Font))
{
return "ttf";
}
else if (type == typeof(AudioClip))
{
return "ogg";
}
else if (type == typeof(GameObject))
{
return "prefab";
}
else if (type == typeof(TextAsset))
{
return "bytes";
}
else if (type == typeof(Texture2D) || type == typeof(Sprite))
{
return "png";
}
return "";
}
}

4.ab包加载模块:和LoadModule 相比,1.ABLoadModule 需要在初始化前加载manifest文件。2.需要在加载资源前加载ab包以及a包的依赖包。3.需要提供卸载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
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/// <summary>
/// 使用ab加载资源(Editor模式下、线上平台)
/// </summary>
public class ABLoadModule : ILoadModule
{
public int SyncCount; // 同步加载并发数
private AssetBundleManifest m_manifest = null;
private HashSet<string> m_bundleNames = new HashSet<string>();
// 加载队列
List<Loader> m_loadings = new List<Loader>();//正在加载
List<Loader> m_loaderQueue = new List<Loader>();//等待加载
Dictionary<string, AssetBundleLoader> m_dicAllLoader = new Dictionary<string, AssetBundleLoader>();

float m_lastClear = 0; // 上一次清除时间

public void Init(int syncCout)
{
SyncCount = syncCout;
LoadManifest();
}

public void UnInit()
{
for (int i = 0; i < m_loadings.Count; i++)
{
m_loadings[i].Stop();
}
m_loadings.Clear();

m_loaderQueue.Clear();
ABCachePool.Instance.ClearAllCache();
}

private List<int> m_lstRmTemp = new List<int>();
public void Update(float dt)
{
for (int i = m_loadings.Count - 1; i >= 0; i--)
{
Loader loader = m_loadings[i];
loader.Update(dt);
if (loader.IsFinish)
{
if (loader.Type == Loader.LoaderType.BUNDLE)
{
AssetBundleLoader bLoader = loader as AssetBundleLoader;
if (m_dicAllLoader.ContainsKey(bLoader.BundleName))
{
m_dicAllLoader.Remove(bLoader.BundleName);
}
}
m_loadings.RemoveAt(i);
LoaderPool.Instance.RecycleLoader(loader);
}
}

int remain = Mathf.Min(SyncCount - m_loadings.Count, m_loaderQueue.Count) ;

for (int i = 0; i < m_loaderQueue.Count; i++)
{
Loader loader = m_loaderQueue[i];
if (loader.Type == Loader.LoaderType.BUNDLE)
{
AssetBundleLoader bLoader = loader as AssetBundleLoader;
if (!bLoader.IsPrepareToLoad())
{
continue; //如果有依赖未加载完直接跳过
}
}
else if (loader.Type == Loader.LoaderType.BUNDLEASSET)
{
BundleAssetLoader bLoader = loader as BundleAssetLoader;
if (!bLoader.IsPrepareToLoad())
{
continue; //Asset是否准备好加载
}
}

m_loadings.Add(loader);
loader.Load();
loader.Update(dt);
m_lstRmTemp.Add(i);
if (m_lstRmTemp.Count >= remain)
{
break;
}
}

if (m_lstRmTemp.Count > 0)
{
for (int i = m_lstRmTemp.Count - 1; i >= 0; i--)
{
m_loaderQueue.RemoveAt(m_lstRmTemp[i]);
}
m_lstRmTemp.Clear();
}

UpdateAssetBundleCache();
}

// 更新AssetBundle缓存(主要执行定时清理)
public void UpdateAssetBundleCache()
{
// 每5秒回收一次
if (Time.realtimeSinceStartup - m_lastClear < 5)
{
return;
}

m_lastClear = Time.realtimeSinceStartup;

/// 检查无引用的AB节点
ABCachePool.Instance.ClearNoneRefCache(true);
}

public void Clear()
{
ABCachePool.Instance.ClearNoneRefCache(false);

Resources.UnloadUnusedAssets();
}

#region LoadManifest
// 加载资源清单
public void LoadManifest()
{
if (m_manifest != null)
{
Object.DestroyImmediate(m_manifest, true);
m_manifest = null;
}

m_bundleNames.Clear();

string manifestName = FileHelper.MANIFEST_NAME;//manifest文件名
string strFullPath = FileHelper.SearchFilePath(manifestName);//获取manifest路径

AssetBundleLoader bLoader = LoaderPool.Instance.GetLoader<AssetBundleLoader>();
bLoader.Init(strFullPath, manifestName, null, delegate (object data) {
AssetBundleCache ab = data as AssetBundleCache;
if (ab != null)
{
m_manifest = (AssetBundleManifest)ab.LoadAsset("AssetBundleManifest", typeof(AssetBundleManifest));
}

ABCachePool.Instance.UnReferenceCache(manifestName, true); // 不走统一接口是因为manifest文件没有后缀

if (m_manifest != null)
{
string[] bundles = m_manifest.GetAllAssetBundles();

for (int i = 0; i < bundles.Length; ++i)
{
m_bundleNames.Add(bundles[i]);
}
}
}, false);
bLoader.Load();
}
#endregion

public void LoadPrefab(string strPath, string name, LoadedCallback onLoaded, bool async = true)
{
LoadAssetFromBundle(strPath, name, typeof(GameObject), onLoaded, async);
}

public void LoadMusic(string name, LoadedCallback onLoaded, bool async = true)
{
string path = string.Format("music/{0}", name);
LoadAssetFromBundle(path, name, typeof(AudioClip), onLoaded, async);
}

public void LoadFont(string name, LoadedCallback onLoaded, bool async = true, bool inBundle = false)
{
string path = string.Format("ui/font/{0}", name);
LoadAssetFromBundle(path, name, typeof(Font), onLoaded, async);
}

#region LoadScene
public void LoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded)
{
var activeSceneName = SceneManager.GetActiveScene().name;
// 如果当前场景是要加载的场景 直接返回
if (activeSceneName == name)
{
if (onLoaded != null)
{
onLoaded(null);
}
return;
}

if (isAdditive)
{
RealLoadScene(name, scenePath, isAdditive, onLoaded);
}
else //大场景先加载idle过渡
{
RealLoadScene(name, scenePath, isAdditive, onLoaded);
}
}

private void RealLoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded)
{
string abPath = "scenes/" + name;
LoadAssetBundle(abPath, delegate (object data) {
if (data == null)
{
CallFunc_LoadedBack(onLoaded, null);
return;
}
__LoadScene(name, scenePath, isAdditive, onLoaded); //场景的bundle在SceneLoader中自动卸载
});
}

private void __LoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded, bool async = true)
{
SceneLoader sLoader = LoaderPool.Instance.GetLoader<SceneLoader>();
sLoader.Init(name, scenePath, isAdditive, onLoaded, async);
StartLoad(sLoader, true);
}
#endregion

//从Bundle中加载资源
public void LoadAssetFromBundle(string path, string name, System.Type type, LoadedCallback onLoaded, bool async = true)
{
LoadAssetBundle(path, (data) => {
AssetBundleCache abCache = data as AssetBundleCache;
if (abCache == null)
{
Debug.LogWarningFormat("LoadAssetFromBundle, load ab fail:{0}", path);
CallFunc_LoadedBack(onLoaded, null);
return;
}

// 开启任务去做加载
BundleAssetLoader assetLoader = LoaderPool.Instance.GetLoader<BundleAssetLoader>();
assetLoader.Init(abCache, name, type, onLoaded, async);
StartLoad(assetLoader, async);
}, async);
}

// 加载AssetBundle(先从persistentData读,没有找到则从streamingAssets读,带后缀)
public void LoadAssetBundle(string path, LoadedCallback onLoaded, bool async = true)
{
string name = FileHelper.GenBundlePath(path);
if (!HasAssetBundle(name))
{
if (onLoaded != null)
{
onLoaded(null);
}
return;
}

// 加载依赖
LoadDependencies(name, async);

// 检查是否有缓存 有缓存说明依赖资源也是加载过了的
AssetBundleCache abCache = ABCachePool.Instance.ReferenceCacheByName(name);
if (abCache != null)
{
if (onLoaded != null)
{
onLoaded(abCache);
}
return;
}

string fullpath = FileHelper.SearchFilePath(name);
AssetBundleLoader bLoader = null;
m_dicAllLoader.TryGetValue(name, out bLoader);
if (bLoader == null)
{
string[] dependencies = m_manifest.GetDirectDependencies(name);

bLoader = LoaderPool.Instance.GetLoader<AssetBundleLoader>();
bLoader.Init(fullpath, name, dependencies, onLoaded, async);
m_dicAllLoader.Add(name, bLoader);
StartLoad(bLoader, async);
}
else
{
if (onLoaded != null)
{
bLoader.AddLoadedCallback(onLoaded);
}
else
{
bLoader.AddReferenced();
}
}
}

// 依赖
// 加载依赖
//asyncInFact 实际加载方式,如果依赖bundle是异步加载并且正在加载中,那么整个bundle的加载方式变成异步加载
void LoadDependencies(string name, bool async)
{
if (m_manifest == null)
{
return;
}

string[] dependencies = m_manifest.GetDirectDependencies(name);
for (int i = 0; i < dependencies.Length; ++i)
{
LoadAssetBundle(dependencies[i], null, async);
}
}

void StartLoad(Loader loader, bool async, bool toHead = false)
{
// 异步加载或者加载还未准备好,则当做异步处理,外部控制是否加入队列开头
// 同步加载,并且已经具备加载条件,则直接调用Load进行加载
if (async || !loader.IsPrepareToLoad())
{
if (toHead)
{
m_loaderQueue.Insert(0, loader);
}
else
{
m_loaderQueue.Add(loader);
}
}
else
{
m_loadings.Add(loader);
loader.Load();
}
}

public bool HasAssetBundle(string path)
{
path = path.Replace("/", "_").ToLower();
return m_bundleNames.Count == 0 || m_bundleNames.Contains(path) || string.Equals(path, FileHelper.MANIFEST_NAME);
}

// 卸载关卡场景
public void UnloadLevelScene(string sceneName, bool immediate)
{
SceneManager.UnloadSceneAsync(sceneName);
UnloadSceneAssetBundle(sceneName, immediate);
}

// 卸载场景的AssetBundle
public void UnloadSceneAssetBundle(string sceneName, bool immediate)
{
if (Config.Instance.UseAssetBundle)
{
string strABPath = "scenes/" + sceneName;
UnloadAssetBundle(strABPath, immediate);
}
}

// 卸载AssetBundle
public void UnloadAssetBundle(string path, bool immediate = false)
{
string name = FileHelper.GenBundlePath(path);
AssetBundleCache cache = ABCachePool.Instance.UnReferenceCache(name, immediate);
if (cache != null)
{
UnloadDependencies(name, immediate);
}
}

// 卸载依赖
void UnloadDependencies(string name, bool immediate)
{
if (m_manifest == null)
{
return;
}

string[] dependencies = m_manifest.GetDirectDependencies(name);
for (int i = 0; i < dependencies.Length; ++i)
{
UnloadAssetBundle(dependencies[i], immediate);
}
}

private void CallFunc_LoadedBack(LoadedCallback callback, object data)
{
if (callback != null)
{
callback(data);
}
}
}