对象池基本介绍

资源管理对象池

类对象池

提前缓存一些基础类的对象,在其他管理类使用时直接从类对象池中取出。

类对象池需要自行实现还原的方法,因为每个具体的类不一样,所以每个类的变量修改后返回到对象池时需要自行重置

在下面的AssetBundle Manager中,AssetBundleItem这个由对象池管理的类就自己实现了一个Reset方法

需要重置的原因是我们的对象池使用Stack<T>,每一次Pop出来的对象都必须的是一样的

AssetBundle管理

读取配置表和依赖加载的管理

资源池管理

资源池管理(ResouceManager)是Asset Bundle管理的上层。

其中异步加载通过使用协程和循环来控制每一帧资源加载的容量来流畅加载。

资源池重点处理的是资源,图片、声音、视频、文字等,还包括未实例化的Prefab。

资源池为上层提供资源加载的接口。

对象池管理

对象池管理(ObjectManager)是资源池管理的上层。

针对需要实例化的对象进行缓存,根据对象是否常用来决定是否放进对象池中。其中异步加载需要实现取消加载,对象池中的对象可能在加载中途不需要加载了。

类对象池

新建ClassObjectPool脚本

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
using System.Collections.Generic;

public class ClassObjectPool<T> where T : class, new()
{
protected Stack<T> m_Pool = new Stack<T>();
protected int m_MaxCount = 0;//如果MaxCount<=0,则无限缓存
protected int m_NoRecycleCount = 0;

public ClassObjectPool(int maxCount)
{
m_MaxCount=maxCount;
for (int i = 0; i < maxCount; i++)
{
m_Pool.Push(new T());
}
}
public T Spawn(bool createIfEmpty)
{
if (m_Pool.Count > 0)
{
T rtn = m_Pool.Pop();
if (rtn == null)//??
{
if(createIfEmpty)
{
rtn = new T();
}
}
m_NoRecycleCount++;
return rtn;
}
else
{
if (createIfEmpty)
{
T rtn = new T();
m_NoRecycleCount++;
return rtn;
}
}
return null;
}
public bool Recycle(T obj)
{
if(obj == null) { return false; }
m_NoRecycleCount--;
if(m_Pool.Count >= m_MaxCount && m_MaxCount>0)
{
obj = null;//置空交给GC
return false;
}
m_Pool.Push(obj);
return true;
}
}

ObjectPoolManager

单例模板

1
2
3
4
5
public class SingletonPattern<T> where T : new() 
{
public static T Instance => _instance ??= new T();
private static T _instance;
}
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
using System;
using System.Collections.Generic;

public class ObjectPoolManager:SingletonPattern<ObjectPoolManager>
{
#region 类对象池的使用
protected Dictionary<Type, object> m_ClassPoolDic = new Dictionary<Type, object>();

/// <summary>
/// 创建类对象池
/// </summary>
public ClassObjectPool<T> GetOrCreatClassPool<T>(int maxCount) where T : class, new()
{
Type type = typeof(T);
object outObj = null;
if (!m_ClassPoolDic.TryGetValue(type, out outObj) || outObj == null)
{
ClassObjectPool<T> newPool = new ClassObjectPool<T>(maxCount);
m_ClassPoolDic.Add(type, newPool);
return newPool;
}
return outObj as ClassObjectPool<T>;
}
#endregion
}

AssetBundleManager

  1. 读取Asset Bundle配置表
  2. 设置中间类进行引用计数
  3. 根据路径加载Asset Bundle
  4. 根据路径卸载Asset Bundle
  5. 为Resource Manager提供加载中间类,根据中间类释放资源,查找中间类等方法。
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
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;

public class AssetBundleManager : SingletonPattern<AssetBundleManager>
{
//资源关系字典,可以根据Crc来找到对应资源块
protected Dictionary<uint,ResourceItem> m_ResourceItemDic = new Dictionary<uint,ResourceItem>();
//储存已加载的AB包,key为根据包名生成的Crc
protected Dictionary<uint,AssetBundleItem> m_AssetBundleItemDic = new Dictionary<uint,AssetBundleItem>();
//AssetBundleItem类对象池
protected ClassObjectPool<AssetBundleItem> m_AssetBundleItemPool = ObjectPoolManager.Instance.GetOrCreatClassPool<AssetBundleItem>(500);
/// <summary>
/// 加载AB配置表
/// </summary>
public bool LoadAssetBundleConfig()
{
m_ResourceItemDic.Clear();
string configPath = Application.streamingAssetsPath + "/assetbundleconfig";
AssetBundle configAB = AssetBundle.LoadFromFile(configPath);
TextAsset textAsset = configAB.LoadAsset<TextAsset>("AssetBundleConfig");//??
if (textAsset == null )
{
Debug.LogError("AssetBundleConfig isn't exist");
return false;
}
using (MemoryStream ms = new MemoryStream(textAsset.bytes))
{
BinaryFormatter bf = new BinaryFormatter();
AssetBundleConfig abConfig = (AssetBundleConfig)bf.Deserialize(ms);

for (int i = 0; i < abConfig.ABBasesList.Count; i++)
{
ABBase abBase = abConfig.ABBasesList[i];
ResourceItem resItem = new ResourceItem
{
m_Crc = abBase.Crc,
m_AssetName = abBase.AssetName,
m_AssetBundleName = abBase.ABName,
m_DependAssetBundle = abBase.ABDependence,
};
if(m_ResourceItemDic.ContainsKey(resItem.m_Crc))
{
Debug.LogError("重复的Crc 资源名:" + resItem.m_AssetName + "AB包名:" + resItem.m_AssetBundleName);
}
else
{
m_ResourceItemDic.Add(resItem.m_Crc, resItem);
}
}
return true;
}
}
/// <summary>
/// 根据路径的crc加载中间类ResourceItem
/// </summary>
public ResourceItem LoadResourceAssetBundle(uint crc)
{
ResourceItem resItem = null;
if(!m_ResourceItemDic.TryGetValue(crc, out resItem) || resItem == null)
{
Debug.LogErrorFormat("LoadResourceAssetBundle error: can not find crc {0} in AssetBundleConfig",crc.ToString());
return resItem;
}

resItem.m_AssetBundle = LoadAssetBundle(resItem.m_AssetBundleName);
if(resItem.m_DependAssetBundle != null)
{
resItem.m_DependAssetBundle.ForEach(abName =>
{
LoadAssetBundle(abName);
});
}
return resItem;
}
/// <summary>
/// 加载AssetBundle并维护AssetBundleItem中间类
/// </summary>
private AssetBundle LoadAssetBundle(string abName)
{
AssetBundleItem abItem = null;
uint abCrc = CRC32.GetCRC32(abName);
if(!m_AssetBundleItemDic.TryGetValue(abCrc, out abItem))
{
AssetBundle assetBundle = null;
string fullPath = Application.streamingAssetsPath + "/" + abName;
#if UNITY_ANDROID
assetBundle = AssetBundle.LoadFromFile(fullPath);
#else
if(File.Exists(fullPath))
{
assetBundle = AssetBundle.LoadFromFile(fullPath);
}
#endif
if (assetBundle == null)
{
Debug.LogError("Load AssetBundle Error:" + fullPath);
}
abItem = m_AssetBundleItemPool.Spawn(true);
abItem.assetBundle = assetBundle;
abItem.refCount++;
m_AssetBundleItemDic.Add(abCrc, abItem);
}
else
{
abItem.refCount++;
}
return abItem.assetBundle;
}
/// <summary>
/// 释放资源,并不清除m_ResourceItemDic,因为每次LoadAssetBundleCfg都会自动清除
/// </summary>
public void ReleaseAsset(ResourceItem item)
{
if (item == null)
return;
if (item.m_DependAssetBundle != null && item.m_DependAssetBundle.Count > 0)
{
for (int i = 0; i < item.m_DependAssetBundle.Count; i++)
{
UnloadAssetBundle(item.m_DependAssetBundle[i]);
}
}
UnloadAssetBundle(item.m_AssetBundleName);
}
private void UnloadAssetBundle(string abName)
{
uint abCrc = CRC32.GetCRC32(abName);
AssetBundleItem abItem = null;
if(m_AssetBundleItemDic.TryGetValue(abCrc,out abItem) && abItem != null)
{
abItem.refCount--;
if(abItem.refCount <= 0 && abItem.assetBundle != null)
{
abItem.assetBundle.Unload(true);
abItem.Reset();
m_AssetBundleItemPool.Recycle(abItem);
m_AssetBundleItemDic.Remove(abCrc);
}
}
}
/// <summary>
/// 根据crc查找ResourceItem
/// </summary>
public ResourceItem FindResourceItem(uint crc)
{
ResourceItem item = null;
m_ResourceItemDic.TryGetValue(crc, out item);
return item;
}

}
/// <summary>
/// 记录AssetBundle的引用计数的中间类,方便阻止重复加载AB和卸载AB,此中间类使用对象池来优化
/// </summary>
public class AssetBundleItem
{
public AssetBundle assetBundle = null;
public int refCount;

public void Reset()
{
assetBundle=null;
refCount = 0;
}
}

//每一个读取出来的AssetBundle,会包含多种Asset资源,我们需要指定资源类型和资源名才能读取到这些资源
//每一个资源对应一个Crc码,这个码是打包时根据资源在编辑器中的路径生成的
//在AB包中的贴图材质等资源是通过manifest加载,是不写在配置里面的
/// <summary>
/// 用来读取AB配置的资源数据
/// </summary>
public class ResourceItem
{
//该资源路径的crc
public uint m_Crc = 0;
//该资源的文件名
public string m_AssetName = string.Empty;
//该资源所在的AssetBundle名字
public string m_AssetBundleName = string.Empty;
//该资源所依赖的AssetBundle名字
public List<string> m_DependAssetBundle = null;
//该资源加载完的AB包
public AssetBundle m_AssetBundle = null;
//----------------------------------------------
//资源对象,在Resource Manager使用
public Object m_Obj = null;
//资源GUID
public int m_GUID = 0;
//资源最后所使用的时间
public float m_LastUseTime = 0.0f;
//引用计数
protected int m_RefCount = 0;
public int RefCount
{
get { return m_RefCount; }
set
{
m_RefCount = value;
if (m_RefCount < 0)
{
Debug.LogError("refcount < 0" + m_RefCount + " ," + (m_Obj != null ? m_Obj.name : "name is null"));
}
}
}
}