Unity对象池的学习

Unity对象池的学习

技术概述

对象池在Unity3D中是极为重要的技术,在遇到需要大量重复创建、销毁的对象时,对象池可以将其存放入池中,反复利用,从而尽可能的重复使用内存中驻留的资源。

技术详述

对象池的典型用法就是射击游戏中的子弹。在不使用对象池时,子弹的“一生”是这样的:创建→产生作用→销毁;而使用对象池后,子弹的“一生”是这样的:从对象池中取出使用→产生作用→存放进对象池→从对象池中取出使用···
显而易见的,该方法能避免重复对象的创建、销毁过程,节省内存空间的使用。以下为一个子弹对象池的大致创建过程

首先是对象池创建

    //prefabPool = new PrefabPool(Resources.Load<Transform>("xxx"));加载本地预制
    //prefabPool.cullDespawned = true;自动清理对象池
    public static BulletPool bullet;//该部分用于初始化此对象池的配置
    public GameObject bulletObj;
    public int pooledAmount = 5;//初始化对象池中对象数量
    public bool lockPoolSize = false;//取消锁定对象池大小
    private List<GameObject> pooledObjects;//创建对象池链表
    private int currentIndex = 0;
    void Awake()
    {
        bullet = this;//实例化对象池
    }

在start()中初始化对象池链表

void Start()
    {
        pooledObjects = new List<GameObject>();
        for (int i = 0; i < pooledAmount; ++i)
        {
            GameObject obj = Instantiate(bulletObj);//创建子弹对象
            obj.SetActive(false);//将子弹对象的激活状态Active设置为false
            pooledObjects.Add(obj);//将子弹存放入对象池中
        }

    }

调用该子弹对象池中的可用子弹

public GameObject GetPooledObject()
    {
        for (int i = 0; i < pooledObjects.Count; ++i)//遍历对象池以寻找可用子弹
        {
            //从上一次调用的子弹的下一个开始寻找
            //例如上一次发射的子弹是对象池中序号为2的子弹,则本次调用对象池中子弹时从3开始检查是否可用
            int temI = (currentIndex + i) % pooledObjects.Count;

            if (!pooledObjects[temI].activeInHierarchy)//检查该对象的Active状态
            {
                currentIndex = (temI + 1) % pooledObjects.Count;
                return pooledObjects[temI];//如果Active为false则返回调用该对象
            }
        }

        if(!lockPoolSize)//若没有false状态的子弹供我们使用,则生成新的对象并加入对象池
        {
            GameObject obj = Instantiate(bulletObj);
            pooledObjects.Add(obj);
            return obj;
        }

        return null;
    }

遇到的问题和解决方案

在对象池中寻找可用对象时,最初只通过遍历进行地毯式搜索。在实际使用时,经过频繁的调用后,会产生略微影响体验的卡顿,一开始以为是对象过多,机器的机能限制,后来查看到了一篇文章,指明了这一问题产生的原因。
在寻找可用对象时,如果每次遍历都从头开始,如果对象池极大,且先前的对象仍然处于激活状态,我们将需要大量时间用来无谓的遍历,导致卡顿。因此,在代码中记录之前使用的对象序号,并从序号记录的下一个对象开始查找可用对象。
这一方法可以极大程度改善因遍历而浪费的机能、时间,对游戏性能进行优化。

总结

对象池是Unity中对性能优化极为重要的技术。在CPU、内存并非充满"Power"的情况下,对象池是让硬件的无谓重复尽可能降到最低,使有限的硬件资源用在最需要的地方,达到优化效果。
个人看来,对象池最大的优点在于复用、预载这两方面。
复用是对象池的典型特征、灵魂所在;而预载方面,作为玩家试想一下,玩家是愿意在加载界面多花1秒钟,还是在激烈战斗时突然卡顿0.1秒。结果是显而易见的。

参考

【Unity 教程】对象池
Unity 对象池的使用
Unity3D内存管理——对象池(Object Pool)

posted @ 2021-06-28 11:05  穗積  阅读(545)  评论(0编辑  收藏  举报