ObjectPoolManager功能

  1. 同步加载
  2. 异步加载
  3. 预加载
  4. 取消异步加载
  5. 基于离线数据的回收处理

ObjectPoolManager管理的资源和ResouceManager不一样,它的资源都是实例化(Clone)出来的,在游戏里真正显示的。

对象池同步加载

中间类ResouceObj

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
public class ResourceObj
{
public uint m_Crc = 0;
/// <summary>
/// 对应的ResouceItem引用
/// </summary>
public ResourceItem m_ResItem = null;
/// <summary>
/// 实例化(Clone)出来的GameObject
/// </summary>
public GameObject m_CloneObj = null;
/// <summary>
/// 是否跳场景清除
/// </summary>
public bool m_bClear = true;
/// <summary>
/// 储存异步方法的GUID,便于停止异步
/// </summary>
public long m_Guid = 0;
/// <summary>
/// 防止重复释放
/// </summary>
public bool m_AlreadyReleased = false;
public void Reset()
{
m_Crc = 0;
m_ResItem = null;
m_CloneObj = null;
m_bClear = true;
m_Guid = 0;
m_AlreadyReleased = false;
}
}

Resource Manager增加方法

在Resource Manager添加返回ResourceObj的方法

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
    /// <summary>
/// 同步加载资源,针对给ObjectManager的接口
/// </summary>
public ResourceObj LoadResource(string path, ResourceObj resObj)
{
if (resObj == null)
{
return null;
}
uint crc = resObj.m_Crc == 0?CRC32.GetCRC32(path) : resObj.m_Crc;
ResourceItem item = GetCacheResourceItem(crc);
if (item != null)
{
resObj.m_ResItem = item;
return resObj;
}

Object obj = null;
#if UNITY_EDITOR
if (!m_LoadFromAssetBundle)
{
item = AssetBundleManager.Instance.FindResourceItem(resObj.m_Crc);
if (item.m_Obj != null)
{
obj = item.m_Obj;
}
else
{
obj = LoadAssetByEditor<Object>(path);
}
}
#endif
if (obj == null)
{
item = AssetBundleManager.Instance.LoadResourceAssetBundle(crc);
if (item != null && item.m_AssetBundle != null)
{
if (item.m_Obj != null)
{
obj = item.m_Obj;
}
else
{
obj = item.m_AssetBundle.LoadAsset<Object>(item.m_AssetName);
}
}
}
CacheResource(path, ref item, crc, obj);
resObj.m_ResItem = item;
item.m_Clear = resObj.m_bClear;

return resObj;
}

对象池同步加载

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

public class ObjectPoolManager:SingletonPattern<ObjectPoolManager>
{
private Transform RecyclePoolTrans;

public Transform SceneTrans;

//用于缓存对象池,uint对应一种资源,List<ResourceObj>对应着这个资源实例化出来的各种对象
protected Dictionary<uint,List<ResourceObj>> m_ObjectPoolDic = new Dictionary<uint, List<ResourceObj>>();
protected ClassObjectPool<ResourceObj> m_ResourceObjPool = null;
//用于缓存已经从对象池实例化出来的ResourceObj,收回对象池但没销毁的也在这里缓存着
protected Dictionary<int,ResourceObj> m_ResourceObjDic = new Dictionary<int,ResourceObj>();
public void Init(Transform recyclePoolTrans,Transform sceneTrans)
{
m_ResourceObjPool = GetOrCreatClassPool<ResourceObj>(1000);
RecyclePoolTrans = recyclePoolTrans;
SceneTrans = sceneTrans;
}
protected ResourceObj GetObjectFromDicCache(uint crc)
{
List<ResourceObj> resObjList = null;
if (m_ObjectPoolDic.TryGetValue(crc, out resObjList) && resObjList != null && resObjList.Count>0)
{
ResourceManager.Instance.IncreaseResourceRef(crc);
ResourceObj resObj = resObjList[0];
resObjList.RemoveAt(0);
GameObject obj = resObj.m_CloneObj;
if(!System.Object.ReferenceEquals(obj, null))//更高效的判空方法
{
#if UNITY_EDITOR
if (obj.name.EndsWith("(Recycle)"))
{
obj.name = obj.name.Replace("(Recycle)", "");
}
#endif
}
return resObj;
}
return null;
}
/// <summary>
/// 同步实例化对象
/// </summary>
/// <param name="path">对象路径</param>
/// <param name="setSceneObj">是否在场景设置父物体</param>
/// <param name="bClear">是否换场景销毁</param>
/// <returns></returns>
public GameObject InstantiateObject(string path,bool setSceneObj = false, bool bClear = true)
{
uint crc = CRC32.GetCRC32(path);
ResourceObj resourceObj = GetObjectFromDicCache(crc);
if (resourceObj == null)
{
resourceObj = m_ResourceObjPool.Spawn(true);
resourceObj.m_Crc = crc;
resourceObj.m_bClear = bClear;
resourceObj = ResourceManager.Instance.LoadResource(path, resourceObj);
if(resourceObj.m_ResItem.m_Obj != null)
{
resourceObj.m_CloneObj = GameObject.Instantiate(resourceObj.m_ResItem.m_Obj) as GameObject;
}
}
//设置父物体很消耗性能
if(setSceneObj)
{
resourceObj.m_CloneObj.transform.SetParent(SceneTrans,false);
}
int tempGUID = resourceObj.m_CloneObj.GetInstanceID();
if (!m_ResourceObjDic.ContainsKey(tempGUID))
{
m_ResourceObjDic.Add(tempGUID, resourceObj);
}
return resourceObj.m_CloneObj;
}

#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

}

对象池资源释放

先在Resource Manager中添加为对象池释放资源的API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/// <summary>
/// 根据ResourceObj卸载资源
/// </summary>
/// <param name="resObj">需要释放的ResourceObj</param>
/// <param name="destroyObj">是否完全释放</param>
/// <returns>是否成功释放</returns>
public bool ReleaseResource(ResourceObj resObj, bool destroyObj = false)
{
if(resObj == null)
return false;
ResourceItem item = null;
if (!AssetDic.TryGetValue(resObj.m_Crc, out item) || item == null)
{
Debug.LogError("AssetDic里不存在该资源:" + resObj.m_CloneObj.name + "可能释放了多次");

}
GameObject.Destroy(resObj.m_CloneObj.gameObject);
item.RefCount--;
DestroyResourceItem(item, destroyObj);
return true;
}

再在ObjectPool Manager中添加回收对象的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
    /// <summary>
/// 回收对象池实例化出来的对象
/// </summary>
/// <param name="obj">从对象池实例化出来的对象</param>
/// <param name="maxCacheCount">设定对象池绘回收最大值,默认无限</param>
/// <param name="destroyCache">是否完全清理资源</param>
/// <param name="hasRecycleParent">是否设定回收的父物体</param>
public void ReleaseObject(GameObject obj, int maxCacheCount = -1, bool destroyCache = false, bool hasRecycleParent = true)
{
if (obj == null)
{
return;
}
ResourceObj resObj = null;
int tempGUID = obj.GetInstanceID();
if(!m_ResourceObjDic.TryGetValue(tempGUID, out resObj))
{
Debug.Log(obj.name + "对象可能不是由对象池创建");
return;
}
if(resObj == null)
{
Debug.LogError("缓存的ResourceObj为空");
}
if(resObj.m_AlreadyReleased)
{
Debug.LogError("此对象已经放回对象池,请检查是否清空引用");
return;
}
#if UNITY_EDITOR
obj.name += "(Recycle)";
#endif
List<ResourceObj> list = null;
if (maxCacheCount == 0)//直接清理对象
{
m_ResourceObjDic.Remove(tempGUID);
ResourceManager.Instance.ReleaseResource(resObj, destroyCache);
resObj.Reset();
m_ResourceObjPool.Recycle(resObj);
}
else//回收到对象池
{
if(!m_ObjectPoolDic.TryGetValue(resObj.m_Crc, out list) || list == null)
{
list = new List<ResourceObj>();
m_ObjectPoolDic.Add(resObj.m_Crc, list);
}
if (resObj.m_CloneObj != null)
{
if (hasRecycleParent)
{
resObj.m_CloneObj.transform.SetParent(RecyclePoolTrans);
}
else
{
resObj.m_CloneObj.SetActive(false);
}
}
if (maxCacheCount < 0 || list.Count < maxCacheCount)
{
list.Add(resObj);
resObj.m_AlreadyReleased = true;
//ResouceManager减少引用计数
ResourceManager.Instance.DecreaseResourceRef(resObj);
}
else
{
m_ResourceObjDic.Remove(tempGUID);
ResourceManager.Instance.ReleaseResource(resObj, destroyCache);
resObj.Reset();
m_ResourceObjPool.Recycle(resObj);
}
}
}

对象池引用计数

我们在Object Pool Manager第一次引用Resource Manager的资源时,Resource Manager会生成ResourceItem中间类供ResourceObj中间类引用,此时ResourceItem中间类会增加一个引用计数。而我们使用Object Pool Manager再次取用Object时,由于我们已经缓存了生成的Object,这一引用计数就不会再增加了,我们需要在ResourceManager添加增加和减少引用计数的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
/// <summary>
/// 根据ResourceObj增加引用计数
/// </summary>
/// <returns>当前资源的引用计数</returns>
public int IncreaseResourceRef(ResourceObj resObj,int refCount = 1)
{
return resObj != null ? IncreaseResourceRef(resObj.m_Crc, refCount) : 0;
}
/// <summary>
/// 根据path\crc增加引用计数
/// </summary>
/// <returns>当前资源的引用计数</returns>
public int IncreaseResourceRef(uint crc = 0, int refCount = 1)
{
ResourceItem item = null;
if (!AssetDic.TryGetValue(crc, out item) || item == null)
{
return 0;
}
item.RefCount += refCount;
item.m_LastUseTime = Time.realtimeSinceStartup;

return item.RefCount;
}
/// <summary>
/// 根据ResourceObj减少引用计数
/// </summary>
/// <returns>当前资源的引用计数</returns>
public int DecreaseResourceRef(ResourceObj resObj, int refCount = 1)
{
return resObj != null ? DecreaseResourceRef(resObj.m_Crc, refCount) : 0;
}
/// <summary>
/// 根据path\crc减少引用计数
/// </summary>
/// <returns>当前资源的引用计数</returns>
public int DecreaseResourceRef(uint crc, int refCount = 1)
{
ResourceItem item = null;
if (!AssetDic.TryGetValue(crc, out item) || item == null)
{
return 0;
}
item.RefCount -= refCount;

return item.RefCount;
}

接着在Object Pool Manager的GetObjectFromDicCache方法体内调用

1
2
3
4
5
6
7
8
9
10
protected ResourceObj GetObjectFromDicCache(uint crc)
{
List<ResourceObj> resObjList = null;
if (m_ObjectPoolDic.TryGetValue(crc, out resObjList) && resObjList != null && resObjList.Count>0)
{
ResourceManager.Instance.IncreaseResourceRef(crc);
//...
}
}

在Object Pool Manager的ReleaseObject方法体内调用

1
2
3
4
5
6
7
8
9
  //...
if (maxCacheCount < 0 || list.Count < maxCacheCount)
{
list.Add(resObj);
resObj.m_AlreadyReleased = true;
//ResouceManager减少引用计数
ResourceManager.Instance.DecreaseResourceRef(resObj);
}
//...

ResourceObj对应的ResourceItem的引用计数减少了,但ResourceObj在Object Pool Manger中的m_ResourceObjDic依然缓存着

我们只有在对象池内部进行对象的增加和回收操作时才会调用IncreaseResourceRefDecreaseResourceRef方法,其他的操作就像直接在Resource Manager取用资源一样,不用再多余维护ResourceItem的引用计数。

同步资源加载示例

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
using UnityEngine;

public class GameStart : MonoBehaviour
{
private GameObject m_GameObject;
private void Awake()
{
DontDestroyOnLoad(gameObject);
AssetBundleManager.Instance.LoadAssetBundleConfig();
ResourceManager.Instance.Init(this);//初始化异步加载
ObjectPoolManager.Instance.Init(transform.Find("RecyclePoolTrans"),transform.Find("SceneTrans"));
}
private void Start()
{
//从对象池实例化对象,并且设置父级
m_GameObject = ObjectPoolManager.Instance.InstantiateObject("Assets/Prefabs/Attack.prefab",true);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.A))
{
//从对象池回收对象,但不销毁
ObjectPoolManager.Instance.ReleaseObject(m_GameObject);
//在使用上面的方式回收对象时,一定要把对象的引用置空,因为对象并没有真正被清理掉,防止发生重复Release的情况
m_GameObject = null;
}
else if (Input.GetKeyDown(KeyCode.D))
{
//从对象池实例化对象,并且设置父级
m_GameObject = ObjectPoolManager.Instance.InstantiateObject("Assets/Prefabs/Attack.prefab", true);
}
else if (Input.GetKeyDown(KeyCode.S))
{
//从对象池回收对象,完全销毁
ObjectPoolManager.Instance.ReleaseObject(m_GameObject, 0, true);
m_GameObject = null;
}
}
private void OnApplicationQuit()
{
ResourceManager.Instance.ClearCache();
Resources.UnloadUnusedAssets();
}
}