游戏开发的优化过程中接触了对象池,根据网上查阅的资料并在自己的理解上实现了一种面向全局的简易对象池.
个人需求
基于UnityEngine.GameObject的对象池,对池内对象进行active操作以避免频繁Instantiate与Destroy带来的高额GC
代码实现
思路:
信息用字典储存 枚举作为键,值为每个子对象池的信息类(ItemPoolInfo)
通过Register/Recycle/Spawn三个函数进行对象池操作
**注:**SetActivate为工具类从元函数,通过比较避免重复Active
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum enum_PoolManagerItems
{
Invalid,
PlayerPrefabs, //Test 1
Bullet, //Test 2
}
public class ObjectPoolManager {
class ItemPoolInfo
{
public GameObject go_SpawnItem;
public int i_poolSaveAmount;
public List<GameObject> l_Deactive=new List<GameObject>();
public List<GameObject> l_Active=new List<GameObject>();
}
static Transform tf_PoolSpawn=new GameObject("PoolSpawn").transform;
static Transform tf_PoolRegist = new GameObject("PoolRegist").transform;
static Dictionary<enum_PoolManagerItems, ItemPoolInfo> d_ItemInfos = new Dictionary<enum_PoolManagerItems, ItemPoolInfo>();
public static GameObject Spawn(enum_PoolManagerItems identity,Transform toTrans)
{
if (!d_ItemInfos.ContainsKey(identity))
{
Debug.LogWarning("Null Identity:" + identity + "Registed");
return null;
}
ItemPoolInfo info = d_ItemInfos[identity];
GameObject obj;
if (info.l_Deactive.Count > 0) //池内有剩余拿出使用
{
obj = info.l_Deactive[0];
info.l_Deactive.RemoveAt(0);
}
else //否则生成新对象
{
obj = GameObject.Instantiate(info.go_SpawnItem,tf_PoolSpawn);
}
info.l_Active.Add(obj);
obj.SetActivate(true);
obj.transform.SetParent(toTrans==null? tf_PoolSpawn:toTrans);
obj.transform.localPosition = Vector3.zero;
return obj;
}
public static void Register(GameObject item, enum_PoolManagerItems identity,int poolSaveAmount)
{
if (d_ItemInfos.ContainsKey(identity))
{
Debug.LogError("Same Element Already Registed:" + d_ItemInfos[identity].go_SpawnItem.name + "/" + item.name);
return;
}
d_ItemInfos.Add(identity,new ItemPoolInfo());
item.transform.SetParent(tf_PoolRegist);
item.SetActivate(false);
ItemPoolInfo info = d_ItemInfos[identity];
info.go_SpawnItem = item;
info.i_poolSaveAmount = poolSaveAmount;
GameObject go;
for (int i = 0; i < info.i_poolSaveAmount; i++) //生成足数池内对象
{
go = GameObject.Instantiate(info.go_SpawnItem,tf_PoolSpawn);
info.l_Deactive.Add(go);
}
}
public static void Recycle(GameObject obj, enum_PoolManagerItems identity)
{
if (!d_ItemInfos.ContainsKey(identity))
{
Debug.LogWarning("Null Identity Of GameObject:"+obj.name+"/"+identity+"Registed");
return;
}
ItemPoolInfo info = d_ItemInfos[identity];
info.l_Active.Remove(obj);
if (info.l_Deactive.Count >= info.i_poolSaveAmount) //池内对象数大于最大保存数直接销毁
{
GameObject.Destroy(obj);
}
else //反之入池保存
{
obj.SetActivate(false);
obj.transform.SetParent(tf_PoolSpawn);
info.l_Deactive.Add(obj);
}
}
}
使用范例:
public class TestScript : MonoBehaviour
{
private void Awake()
{
GameObject go_test1 = transform.Find("Cube").gameObject;
ObjectPoolManager.Register(go_test1, enum_PoolManagerItems.Bullet, 3);
GameObject go_spawn = ObjectPoolManager.Spawn(enum_PoolManagerItems.Bullet, this.transform);
GameObject go_test2 = transform.Find("Sphere").gameObject;
ObjectPoolManager.Register(go_test2, enum_PoolManagerItems.PlayerPrefabs, 5);
for (int i = 0; i < 11; i++)
{
go_spawn = ObjectPoolManager.Spawn( enum_PoolManagerItems.PlayerPrefabs,this.transform);
ObjectPoolManager.Recycle(go_spawn, enum_PoolManagerItems.PlayerPrefabs);
}
}
}
运行效果:
未来的升级方案(可能):
1.将ItemPoolInfo拓展为针对Monobehaviour子类的模板类
2.将保存物体的Transform在Register时分配到ItemPoolInfo中,并提供Unregsiter函数清空单个ItemPoolInfo
3.同消息收发器,将键枚举也拓展为模板类
PS:但是目前还没有遇到这些奇奇怪怪的需求,就暂时不进行升级了.
该文章只能提供思路,个人学疏才浅,无法保证是否会产生性能问题.
--StriteR 2019/3/21