游戏开发中,不可避免的用到了对象池。如果一个对象频繁的创建、使用、销毁,就需要考虑用对象池。写之前简单搜了一下“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,就可以测试。