Unity设计模式之对象池(缓存池)模式

对象池模式是我们在Unity开发中会经常用到的几种设计模式之一,最常见的例子就是在FPS游戏中子弹的创建与销毁或者说在无双游戏中敌人的创建和销毁都会使用到对象池模式。

首先我们先看一下为什么会使用到对象池模式,拿最简单的一个例子,在FPS游戏中,我们按下鼠标开火生成子弹,子弹快速移动后有些可能击中敌人,游戏可能打到墙壁是上。但这些子弹在结束碰撞后都应该被销毁。但是如果频繁地创建和销毁游戏对象的话,会导致游戏的性能损失。特别是那种比较繁多的游戏对象 。

对象池模式所解决的就是创建一个类似于柜子的管理器,当我们需要使用该游戏对象也就是资源的时候从柜子中拿出来,使用完毕后就将游戏对象放回,这样就避免了性能的损失。

下面实现一个简单的对象池

首先它需要一个数据类型去存放游戏对象,把场景中所有的对象归为一类,然后为了让我们在Scene面板中能够清晰看清哪些对象被使用了,哪些被放回来池中,我们还需要一个动态生成池子游戏对象。我们还需要一个PoolManager管理类去给存放所有的数据类型和给外部提供调用的方法。基本代码如下:

1.PoolData

/// <summary>
        /// 对象挂载的父节点
        /// </summary>
        public GameObject fatherObj;
        /// <summary>
        /// 对象容器
        /// </summary>
        public List<GameObject> poolList;

        /// <summary>
        /// 给池内的数据对象创建一个父对象 并作为pool对象的子物体
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="poolObj"></param>
        public PoolData(GameObject obj, GameObject poolObj)
        {
            this.fatherObj = new GameObject(obj.name);
            fatherObj.transform.parent = poolObj.transform;
            poolList = new List<GameObject>() { obj };
            obj.transform.parent = fatherObj.transform;
            obj.SetActive(false);
        }

        /// <summary>
        /// 往池子的抽屉中放入对象
        /// </summary>
        /// <param name="obj"></param>
        public void PushObject(GameObject obj) {
            obj.SetActive(false);
            poolList.Add(obj);
            obj.transform.parent= fatherObj.transform;
        }

        /// <summary>
        /// 从池子的抽屉中取对象
        /// </summary>
        /// <returns></returns>
        public GameObject GetObject() { 
            GameObject obj= null;
            obj = poolList[0];
            poolList.RemoveAt(0);
            obj.SetActive(true);
            obj.transform.parent = null;
            return obj;
        }

上面这个池子数据类中一个游戏对象的字段用于存放对象的父节点,有一个List用于存放单个的游戏对象。还要一个构造方法去新建一个List然后将传入的obj存放进List中。还要两个方放分别是从这个数据类型中去获取有对象和将游戏对象添加到数据类型中去。PoolData基本完成的就是如上功能。

2.PoolManager

        public static PoolManager Instance => Singleton<PoolManager>.Instace;
        private GameObject poolObject;
        public void Init  () { }
        
        /// <summary>
        /// 缓存池容器
        /// </summary>
        public Dictionary<string,PoolData> poolDic= new Dictionary<string,PoolData>();

        
        
        public GameObject GetObject(string name)
        {
            GameObject obj=null;
            if (poolDic.ContainsKey(name) && poolDic[name].poolList.Count>0)
            {
                obj = poolDic[name].GetObject();
            }
            else
            {
                obj=GameObject.Instantiate(Resources.Load<GameObject>(name));
                obj.name = name;
            }
            return obj;
        }


        /// <summary>
        /// 将对象放入池中
        /// </summary>
        public void PushObject(string name,GameObject obj) {
            obj.SetActive(false);
            if (poolObject == null)
                poolObject = new GameObject("Pool");
            obj.transform.parent = poolObject.transform;
            if (poolDic.ContainsKey(name))
            {
                poolDic[name].PushObject(obj);
            }
            else
            {
                poolDic.Add(name, new PoolData(obj,poolObject));
            }
        }

      
        public void Clear() { 
            poolDic.Clear();
            poolObject= null;
        }

缓存池管理器中也有一个字段poolObject用于生成Scene面板中的Pool父级,还有一个字典用于存放所有的Data类型。三个方法对应取、放、清。

以上就是最基本的对象池模式的实现。