概念
我们先讲讲对象池是什么,有什么用。在游戏的制作过程中,我们可能会碰到这样的情况,就像现在最火的吃鸡游戏一样,我们有一把枪,开枪的时候射出子弹。每个子弹即一个对象,正常情况,我们的处理方式可能会是,每开一枪,就GameObject.Instantiate()一个新的子弹,当子弹到达极限距离的时候再GameObject.Destroy()销毁它。假设有射出1000发子弹,我们就会执行1000次这样的操作,然而在Unity中Instantiate和Destroy操作,不仅影响性能还容易产生内存碎片,总之就是要尽量少做这种操作。这个时候就有对象池这个概念。
所谓对象池,就是针对需要经常生成消失的对象。我们在对象需要消失的时候不Destroy而是SetActive(false),然后放入池子中(Queue),当需要再次显示一个新的对象的时候,先去池子中看有没有隐藏的对象,有就取出SetActive(true),若池子里没有可用的则再Instantiate。
还是上面的例子,假设我们玩家能看见的子弹只有10发,那么在连续开枪1000次的时候,我们生成第一发子弹的时候,此时对象池为空,我们Instantiate一个子弹,第二到第十发子弹的时候同理,再Instantiate九个子弹,第十一发的时候,前十发子弹还是处于显示状态,池子中依旧为空,我们继续Instantiate一个子弹,当这个子弹射出的时候,我们的第一发子弹就应该无法看见了,我们即可SetActive(false),然后放入对象池中。当第12发子弹的时候,我们这个时候对象池里就有一个可用对象,即第一发子弹,我们取出并显示它,然后将它的起始坐标更新到枪口。第13发子弹之后就如此循环。这样,我们在1000发子弹的时候只需要执行11次Instantiate和Destroy操作,起到优化作用。
实现
注:实现的方式有很多,下面只是个人的一种方式,大家可以针对自己的需求任意修改。
了解了上面的概念之后,实现起来就很方便了。首先我们先生成一个对象池的class,里面的内容就很简单,首先有一个Queue用来存放池子中的对象,然后实现两个方法,一个取对象,一个销毁对象,取对象的时候,若池子中有可用对象则取出一个,若没有则Instantiate一个。销毁对象即将对象SetActive(false)并且放入池子中。代码如下
public class BaseGameObjectPool {
/// <summary>
/// 队列,存放对象池中没有用到的对象,即可分配对象
/// </summary>
protected Queue m_queue;
/// <summary>
/// 对象池中存放最大数量
/// </summary>
protected int m_maxCount;
/// <summary>
/// 对象预设
/// </summary>
protected GameObject m_prefab;
/// <summary>
/// 该对象池的transform
/// </summary>
protected Transform m_trans;
/// <summary>
/// 每个对象池的名称,当唯一id
/// </summary>
protected string m_poolName;
/// <summary>
/// 默认最大容量
/// </summary&g