游戏开发中,不可避免的用到了对象池。如果一个对象频繁的创建、使用、销毁,就需要考虑用对象池。写之前简单搜了一下“unity 对象池”,大多数是对GameObject管理,或者自定义的一个父类。
而且用到对象池的地方很多,被管理的对象种类也很多,可以是GameObject也可以是UIlabel,还可以是buff、特效、等等。都继承一个父类或者写多个对象池管理,显然是不合算的。
各路大神应该都有自己的解决方案,我简单说一下泛型对象池。
泛型对象池优点:
1、避免了频繁的创建和消耗,这是用对象池的目的
2、逻辑简单,即便是重构也花不了多少时间。
3、适用性比较广泛,这是泛型的优点。
缺点:项目里用了3年,也上线跑了1年多,还未发现明显缺点。
百度上有很多关于泛型的文章,大神讲解的也很详细,我就不多说,我们平时用泛型还是比较多的,比如,List<int> test = new List<int>();其中<>包裹的int就是用了泛型。
简单说一下思路。
1、每个对象都去实现一个接口,同时这个接口也是泛型的约束。这个接口应该包括回收和清理,部分情况只有回收就可以满足。
2、对象池本身应该要有上限,对象集合,当前可用,当前已用,获取对象,回收对象,清理所有对象。其中对象集合,有的用列表,有的用字典,这里用了栈(Stack)。
下面展示每个功能的代码:
1、对象接口:IObject
public interface IObject
{
void Reset();
void Cleanup();
}这里不用多说,回收Reset,清理Cleanup。其实功能相同,只不过清理的时候会有一个特殊操作,可以按需求写成一个。
2、对象池管理:ObjectPool
using System.Collections.Generic;
public static class ObjectPool<T> where T : IObject, new()
{
private static int _maxCount = 50;
private static Stack<T> pool;
private static int totalCreated;
static ObjectPool()
{
pool = new Stack<T>();
}
/// <summary>
/// 回收对象
/// </summary>
/// <param name="obj"></param>
public static void Recycle(T obj)
{
obj.Cleanup();
if (pool.Count < maxCount)
{
pool.Push(obj);
}
}
public static int maxCount
{
get
{
return _maxCount;
}
set
{
_maxCount = value;
}
}
/// <summary>
/// 总创建数
/// </summary>
/// <returns></returns>
public static int GetTotalCreated()
{
return totalCreated;
}
/// <summary>
/// 当前可用大小
/// </summary>
/// <returns></returns>
public static int GetSize()
{
return pool.Count;
}
public static T GetObj()
{
T result;
if (pool.Count > 0)
{
result = pool.Pop();
}
else
{
result = new T();
totalCreated++;
}
result.Reset();
return result;
}
public static void Cleanup()
{
pool.Clear();
totalCreated = 0;
}
}核心逻辑就是获取对象和回收。回收时将对象push进栈,获取时,将栈里的pop出来,没有就创建。
3、以buff为例的简单应用的例子
using System.Collections.Generic;
using UnityEngine;
public class ObjectPoolExamples: MonoBehaviour {
private List<Buff> buffList = new List<Buff>();
void Start () {
ObjectPool<Buff>.maxCount = 20;
}
// Update is called once per frame
void Update () {
//点击右键添加buff
if (Input.GetKeyDown(KeyCode.Mouse0))
{
Buff buff = ObjectPool<Buff>.GetObj();
//随机给一些值
buff.id = Random.Range(0, 100);
buff.go = null;
buff.endTime = Time.time + Random.Range(1f, 10f);
}
//时间到了回收buff
for (int i = buffList.Count - 1; i >= 0; i--)
{
if (Time.time - buffList[i].endTime>0)
{
ObjectPool<Buff>.Recycle(buffList[i]);
buffList.RemoveAt(i);
}
}
}
}
public class Buff : IObject
{
//buff的ID
public int id;
//buff的资源
public GameObject go;
//持续时间
public float endTime;
public void Reset()
{
Debug.Log("buff重置");
id = 0;
go = null;
endTime = 0;
}
public void Cleanup()
{
Debug.Log("buff清理--并且抛出一条消息");
id = 0;
go = null;
endTime = 0;
}
}这个例子很简单,实际开发中逻辑复杂无数倍,但核心就是添加和移除buff,buff有明显的实效行,因此用对象池最合适。
逻辑简单,代码也少,就不详细说明了。把代码挨个copy到工程里,随便一个实体挂上ObjectPoolExamples,就可以测试。
















