Unity 一种对象池的实现

9 篇文章 0 订阅

在Untiy中,如果在短时间内有大量GameObject被创建销毁,会造成性能下降,游戏卡顿。

根据线程池的思想,我们可以建立一个对象池来暂存暂时不用的GameObject,要用时再从对象池取出,实现GameObject的复用,减少创建,销毁。

以最近项目中的弹幕池为例。

要点:

1、编写弹幕池类,其拥有一个队列作为池来保存对弹幕GameObject的引用。

2、将该类设为单例,避免多次创建,管理混乱。

3、场景中设有作为弹幕根的Empty GameObject,将不用的弹幕设为其孩子,方便调试时查看。

using UnityEngine;
using System.Collections.Generic;

public class BarragePool
{
    private static BarragePool _instance = new BarragePool();

    //对象池的真正本体,一个存放弹幕GameObject的队列(也可以用其他容器实现)
    Queue<GameObject> pool = new Queue<GameObject>();
    //暂存未激活的不用的弹幕GameObject的场景内对象(需要在场景中预设)
    GameObject PoolRoot;

    //BarragePool作为单例,这是一种简单的实现方式,更高级的实现方式可以参考我另一个博客。
    public static BarragePool Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new BarragePool();
            }
            return _instance;
        }
    }


    /// <summary>
    /// 获取弹幕GameObject
    /// </summary>
    /// <returns></returns>
    public GameObject GetBarrage()
    {
        GameObject result = null;

        //若池中没有弹幕对象
        if (pool.Count == 0)
        {

            //使用"Prefabs/Barrage"的弹幕预设体
            GameObject prefab = Resources.Load<GameObject>("Prefabs/Barrage");

            if (prefab != null)
            {
                //创建弹幕GameObject
                result = GameObject.Instantiate(prefab); 
            }
            else
            {
                //如果未找到"Prefabs/Barrage"的预设体,则报错
                Debug.LogError("load >Prefabs/Barrage< failed!");
                return null;
            }
        }
        else
        {
            //从池中取出已有的弹幕GameObject
            result = pool.Dequeue();
        }

        //激活弹幕GameObject
        result.SetActive(true);
        return result;
    }

    /// <summary>
    /// 回收弹幕GameObject,并放入场景中BarragePoolRoot下
    /// </summary>
    /// <param name="barrage"></param>
    public void ResetBarrage(GameObject barrage)
    {
        //将回收的弹幕设为未激活
        barrage.SetActive(false); 
        //PoolRoot初始化
        if(PoolRoot == null)
        {
            PoolRoot = GameObject.Find("BarragePoolRoot");
            //如果场景中没有BarragePoolRoot就报错
            if (PoolRoot == null)
            {
                Debug.LogError("Cannot find 'BarragePoolRoot'!"); 
            }
        } 
        //放回PoolRoot下
        barrage.transform.SetParent(PoolRoot.transform);
        //收入池中
        pool.Enqueue(barrage);
    }
}

以上的对象池实现对普通的游戏够用了。

如果有更高要求,可以参考线程池的设计:
1、给池加大小限制,超出大小时,拒绝创建新对象,用于维持性能。然后可以设置阻塞队列让被拒绝的“任务”阻塞等待(这个貌似在Untiy这种环境下是不行的),或者直接报错。

2、如果对象有很多初始化的工作(不仅仅是根据预设体创建对象),或者要求扩展性,就去实现对象工厂。

3、给对象设置生存时间,以免长时间不用而空占资源。或者提供销毁方法,调用时销毁一定数量的不用的对象。

4、提供对象池的关闭方法,等待所有阻塞的“任务”执行完后销毁池中对象和池。

5、使用泛型编写对象池,使其能够通用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值