在游戏中经常会需要大量创建与销毁对象(比如子弹),而这样是很消耗性能的,如果我们事先创建一些对象把它们存起来,当需要时就把从里面取,当要销毁时就把它再放进去这样就不用重复大量创建与销毁对象了。
先创建一个类取名为ObjectPool,作为相应的对象池。
其中首先需要一个容器用于存储对象,这里选用队列就很适合,
public Queue<GameObject> pool = new Queue<GameObject>()
然后需要方法预先给容器存放一些对象
public void FillPool()
{
for(int i = 0;i < addSum; i++)
{
PutObject(MonoBehaviour.Instantiate(Prefab));
}
MonoBehaviour.print(Prefab);
MonoBehaviour.print(pool.Count);
}
然后还需要提供基础的Get/Put方法用于存取对象池对象。
public GameObject GetObject()
{
if (pool.Count == 0) FillPool();
GameObject obj = pool.Dequeue();
obj.SetActive(true);
return obj;
}
public void PutObject(GameObject obj)
{
obj.SetActive(false);
obj.transform.parent = poolGO.transform;
pool.Enqueue(obj);
}
最后需要一个构造方法用于初始化,完整代码如下:
public class ObjectPool
{
private int InitSum; //初始数量
private int addSum; //一次填充对象数量
public Queue<GameObject> pool = new Queue<GameObject>();
private GameObject Prefab; //存储对象的Prefab
public static GameObject poolGO; //总对象池管理
public ObjectPool(GameObject Prefab,int InitSum = 10,int addSum = 10)
{
this.Prefab = Prefab;
this.addSum = addSum;
FillPool();
}
//填满对象
public void FillPool()
{
for(int i = 0;i < addSum; i++)
{
PutObject(MonoBehaviour.Instantiate(Prefab));
}
MonoBehaviour.print(Prefab);
MonoBehaviour.print(pool.Count);
}
public GameObject GetObject()
{
if (pool.Count == 0) FillPool();
GameObject obj = pool.Dequeue();
obj.SetActive(true);
return obj;
}
public GameObject GetObject(Vector3 postion, Quaternion rotation)
{
if (pool.Count == 0) FillPool();
GameObject obj = pool.Dequeue();
obj.SetActive(true);
obj.transform.position = postion;
obj.transform.rotation = rotation;
return obj;
}
/// <summary>
/// 将对象放入对象池
/// </summary>
/// <param name="obj">对象</param>
public void PutObject(GameObject obj)
{
obj.SetActive(false);
obj.transform.parent = poolGO.transform;
pool.Enqueue(obj);
}
}
创建玩对象池类后,还需要一个脚本来管理各种不同对象的对象池: 在Hierarchy面板中创建一个Empty取名为Pool,然后给他创建一个脚本取名为PoolScript。 这里我需要两个对象池,一个用于存储角色残影对象,一个用于存储子弹对象,所以我创建了两个ObjectPool实例并设为静态,方便其他脚本访问,代码如下:
public class PoolScript : MonoBehaviour
{
public GameObject PlayerShadowPrefab;
public GameObject BulletPrefab;
public static ObjectPool playerShadowPool;
public static ObjectPool bulletPool;
private void OnEnable()
{
ObjectPool.poolGO = gameObject;
playerShadowPool = new ObjectPool(PlayerShadowPrefab);
bulletPool = new ObjectPool(BulletPrefab,30,5);
}
}
然后需要在相应对象的控制脚本中,将销毁的代码改为放入对象池(PutObject),将创建的代码改为从对象池中取出对象(GetObject)。例如我有子弹的对象池,那么发射子弹的Fire方法就改为:
private void Fire()
{
GameObject newBullet = PoolScript.bulletPool.GetObject(transform.TransformPoint(BulBeginVec), gameObject.transform.rotation * Quaternion.Euler(0, -40, 0));
newBullet.GetComponent<BulletController>().Init(attackPower,bulSpeed);
}
控制子弹的脚本BulletController中就添加如下代码:
//当启用时调用
private void OnEnable()
{
Invoke(nameof(Kill), MaxLiveTime); //3s后消除
}
void OnTriggerEnter(Collider cldOther)
{
this.Kill();
}
private void Kill()
{
PoolScript.bulletPool.PutObject(gameObject);
/*Destroy(gameObject);*/
}
当释放子弹和残影时:
https://hctra.cn/usr/uploads/2020/11/3884290347.gif 对象情况: