在游戏里经常会用到重复资源,最常哪来举例的就是射击游戏中的子弹,像这样大量有共同特征的对象,就用一个容器来统一管理,需要的时候从容器里获取,销毁的时候也并不是真的Destroy,而是回收到容器里,如果容器中没有空闲对象,则创建,这样就可以避免大量的创建、销毁操作,实现代码的高效与低开销。
不同的项目中,可能会根据项目情况对对象池进行不同的优化或简化,不过原理都是一样。而且很多情况是一个项目中并不只有一种重复资源,可能会有很多种,这样就需要多组对象池来管理。如果是每一个对象池,都复制粘贴一遍代码,这无疑是各位大大们最不想看到的。最好是有一个管理器,来统一管理所有的对象池,这里我也根据最近几个小项目写了一个简易的对象池,由管理器来创建或销毁所有的对象池及其池对象,在此做一下总结。
//为了方便后期识别和管理,用枚举来标记对象在池中的状态
//对象的池状态
public enum PoolItemState
{
Work,
Idle
}
//管理对象池,原理也很简单,就是用一个容器,再来统一的管理所有的对象池,用管理器来创建或销毁对象池,以及对池的子对象进行的操作。
public class PoolManager : MonoBehaviour
{
public static PoolManager instance;
//Dictionary查找、添加、删除 速度较快,不过内存占用相对较多
Dictionary<int, PoolItemBase> itemPrefabDic;
Dictionary<int, Transform> workParentDic;
Dictionary<int, Transform> idleParentDic;
Dictionary<int, List<PoolItemBase>> workListDic;
Dictionary<int, List<PoolItemBase>> idleListDic;
void Awake()
{
instance = this;
itemPrefabDic = new Dictionary<int, PoolItemBase>();
workParentDic = new Dictionary<int, Transform>();
idleParentDic = new Dictionary<int, Transform>();
workListDic = new Dictionary<int, List<PoolItemBase>>();
idleListDic = new Dictionary<int, List<PoolItemBase>>();
}
//注册一个对象
public int InitNewPool(PoolItemBase poolItem, Transform parentTrans = null)
{
//判断是否已注册
if (itemPrefabDic.ContainsValue(poolItem))
{
Debug.Log("<color=red> 注册的 poolIndex 已存在 </color>" + poolItem);
return -1;
}
//生成对象池Index,在销毁时某个对象池,长度会缩减,获取的值将会重复,所以不能用itemPrefabDic.Count
//int poolIndex = itemPrefabDic.Count;
int poolIndex = GetNewPoolIndex();
//记录PoolItemBase
itemPrefabDic.Add(poolIndex, poolItem);
//设置子对象的父物体
if (parentTrans == null)
{
//如果未指定父物体,生成
parentTrans = new GameObject(poolItem.name + "_WorkParent_CanDestroy").transform;
parentTrans.parent = transform;
}
workParentDic.Add(poolIndex, parentTrans);
Transform idleParentTrans = new GameObject(poolItem.name + "_IdleParent").transform;
idleParentTrans.gameObject.SetActive(false);
idleParentTrans.parent = transform;
idleParentDic.Add(poolIndex, idleParentTrans);
//用List保存子对象
workListDic.Add(poolIndex, new List<PoolItemBase>());
idleListDic.Add(poolIndex, new List<PoolItemBase>());
//返回新注册对象池的Index
return poolIndex;
}
//获取池对象
public PoolItemBase GetOneItem(int poolIndex)
{
//判断是否在池内
if (!itemPrefabDic.ContainsKey(poolIndex))
{
Debug.Log("<color=red> 获取的 poolIndex 不存在 </color>" + poolIndex);
return null;
}
PoolItemBase tmpGetItem = null;
if (idleListDic[poolIndex].Count > 0)
{
//如果有多余Item
tmpGetItem = idleListDic[poolIndex][0];
tmpGetItem.transform.SetParent(workParentDic[poolIndex]);
idleListDic[poolIndex].RemoveAt(0);
}
else
{
//如果没有,加载
tmpGetItem = Instantiate(itemPrefabDic[poolIndex], transform.position, Quaternion.identity, workParentDic[poolIndex]);
}
workListDic[poolIndex].Add(tmpGetItem);
//初始化
tmpGetItem.InitPool(poolIndex);
//
return tmpGetItem;
}
//回收池对象
public void DestroyOneItem(int poolIndex, PoolItemBase item)
{
if (!itemPrefabDic.ContainsKey(poolIndex))
{
Debug.Log("<color=red> 回收的 poolIndex 不存在 </color>" + poolIndex);
return;
}
//将工作的Item回收
if (!idleListDic[poolIndex].Contains(item))
idleListDic[poolIndex].Add(item);
if (workListDic[poolIndex].Contains(item))
workListDic[poolIndex].Remove(item);
item.transform.SetParent(idleParentDic[poolIndex]);
//重置
item.ResetPool();
}
//清空对象池
public void ClearPool(int poolIndex)
{
if (!itemPrefabDic.ContainsKey(poolIndex))
{
Debug.Log("<color=red> 回收的 pool 不存在 </color>" + poolIndex);
return;
}
//遍历,回收
for (int i = 0; i < workListDic[poolIndex].Count; i++)
{
DestroyOneItem(poolIndex, workListDic[poolIndex][i]);
}
workListDic[poolIndex].Clear();
}
//销毁一个对象池
public void DestroyPool(int poolIndex)
{
if (!itemPrefabDic.ContainsKey(poolIndex))
{
Debug.Log("<color=red> 销毁的的 pool 不存在 </color>" + poolIndex);
return;
}
//销毁/清空
if (workParentDic[poolIndex].name.EndsWith("CanDestroy", System.StringComparison.Ordinal))
Destroy(workParentDic[poolIndex].gameObject);
else
{
ClearPool(poolIndex);
}
Destroy(idleParentDic[poolIndex].gameObject);
//数据移除
itemPrefabDic.Remove(poolIndex);
workParentDic.Remove(poolIndex);
idleParentDic.Remove(poolIndex);
workListDic.Remove(poolIndex);
idleListDic.Remove(poolIndex);
}
int indexNum = 0;
int GetNewPoolIndex()
{
return indexNum++;
}
}
//因为是对池对象的统一管理,给其添加一个父类,关于继承和多态,能搜到各种详细的资料~~
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//池对象父类
public abstract class PoolItemBase : MonoBehaviour
{
protected PoolItemState itemState;
protected int poolIndex;
//初始化
public void InitPool(int poolInde)
{
itemState = PoolItemState.Work;
poolIndex = poolInde;
gameObject.name = "item_" + poolIndex;
Init();
}
//重置,销毁时调用
public void ResetPool()
{
itemState = PoolItemState.Idle;
transform.localPosition = Vector3.zero;
Reset();
}
public abstract void Init();
public abstract void Reset();
}
//到这里对象池就算写完咯,关于其中的数据结构,需要根据实际项目的数据量及数据操作方式,选择合适的方案,如果数据比较多,字典就不是最佳选择了···
//接下来是一个简单的小测试,看一下实际效果
//对象类继承父类,并重写方法
public class CubeItem : PoolItemBase
{
public override void Init()
{
StartCoroutine(Move());
}
public override void Reset()
{
StopAllCoroutines();
}
IEnumerator Move()
{
float timer = 0;
while(timer < 2)
{
transform.position += Vector3.forward * 5;
timer += 0.02f;
yield return new WaitForSeconds(0.02f);
}
PoolManager.instance.DestroyOneItem(poolIndex, this);
}
}
//对象的使用者,注册一个对象池,获取对象池的Index,然后就可以创建对象啦
public class LauncherControl : MonoBehaviour
{
[SerializeField] CubeItem CubePrefab;
int poolIndex;
void Start ()
{
poolIndex = PoolManager.instance.InitNewPool(CubePrefab);
StartCoroutine(LaunchCube());
}
void Update()
{
if (Input.GetMouseButtonDown(1))
PoolManager.instance.ClearPool(poolIndex);
else if (Input.GetMouseButtonDown(2))
PoolManager.instance.DestroyPool(poolIndex);
}
IEnumerator LaunchCube()
{
while(true)
{
PoolManager.instance.GetOneItem(poolIndex);
yield return new WaitForSeconds(0.5f);
}
}
}
感谢大家的鼓励与支持,本人目前正处于我工作 我学习 我快乐的学习阶段,在技术上还需要继续努力,分享的Demo或案例或多或少都存在有待优化的地方,欢迎指点~~~