unity-对象池

简介

在游戏中经常会需要大量创建与销毁对象(比如子弹),而这样是很消耗性能的,如果我们事先创建一些对象把它们存起来,当需要时就把从里面取,当要销毁时就把它再放进去这样就不用重复大量创建与销毁对象了。

实现样例

先创建一个类取名为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);*/
}

实现效果

当释放子弹和残影时:
GIF.gif
对象情况:
use.gif

本文作者:六月丶

本文链接:https://hctra.cn/index.php/archives/700/

版权声明:如无特别声明,本文即为Hello World原创,仅代表个人观点,如要转载请务必注明文章出处。
Last modification:November 25th, 2020 at 08:49 pm
如果觉得我的文章对你有用,请随意赞赏

Leave a Comment