Unity——对象池

前言

     游戏中,经常会大量频繁的创建、销毁游戏对象(子弹、溅血粒子特效等),这会带来一定的性能开销,此时,我们可以利用对象池技术来优化.

实现对象池的步骤

    1. 首先,使用一个数据结构直接存储对象,为了降低装箱、拆箱操作,使用List来存储。
    2. 其次,使用字典Dictionary>来存储多个对象。
    3. 然后,从对象池中提取对象,如果对象池中已经存在,则激活该对象,并且从List集合中移除,否则,创建新的对象(OutPool方法实现该功能)。
    4. 最后,将不用的对象再加入到对象池,加入之前,先判断该对象对应的键是否已经存在,如果存在,就直接添加进对应键的值中,否则,创建新的键值对。(JoinPool方法实现该功能)

代码

using UnityEngine;
using System.Collections.Generic;

/// 
/// 对象池单例类
/// 
public class ObjectPoolSingleton
{
    //存储多个对象(存储一个也可以哦)
    //使用字典存储对象名与对象
    //1、键用的字符串,保存对象的名字,可以通过名字来从Resources文件夹动态加载资源
    //2、用泛型集合直接存储对象,降低装箱、拆箱次数
    private Dictionary> pools;
    #region 单例
    private static ObjectPoolSingleton instance;
    private ObjectPoolSingleton()
    {
        //初始化对象池        
        pools = new Dictionary>();
    }
  
    public static ObjectPoolSingleton Instance
    {
        get { return instance = instance ?? new ObjectPoolSingleton(); }
    }
    #endregion
    /// 
    /// 出对象池(激活对象)
    /// 
    /// 对象的名称
    /// 位置
    /// 旋转
    /// 
    public GameObject OutPool(string objName, Vector3 pos, Quaternion qua)
    {
        //首先根据名称判断字典里有没有该对象
        if (pools.ContainsKey(objName))
        {
            //有,再判断个数(想想,为什么要判断个数,难道还会为0?)
            if (pools[objName].Count > 0)
            {
                //获得第集合第一个对象
                GameObject currentObj = pools[objName][0];
                //提取了,就应该将其从集合中移除
                pools[objName].Remove(currentObj);
                //激活该对象
                currentObj.SetActive(true);
                //初始化该对象的位置、旋转信息
                currentObj.transform.position = pos;
                currentObj.transform.rotation = qua;
                return currentObj;
            }           
        }
        //没有,怎么办呢,创建一个,我们这里是用Resources动态加载要实例化的对象
        GameObject newObj = GameObject.Instantiate(Resources.Load(objName), pos, qua) as GameObject;
        return newObj;
    }
    /// 
    /// 进对象池
    /// 
    /// 要加入的对象
    public void JoinPool(GameObject obj)
    {
        //隐藏对象
        obj.SetActive(false);
        //去掉对象名称后的‘(Clone)’
        string objName = obj.name.Substring(0, obj.name.Length - 7);
        //首先判断字典中是否已经有了该对象
        if (pools.ContainsKey(objName))
        {
            //有了,就添加到对应键的集合里
            pools[objName].Add(obj);
        }
        else
        {
            //没有,添加新的键值对
            pools.Add(objName, new List() {obj});
        }
    }
}


该脚本可以添加到创建的空物体身上,注意游戏场景中必须要有带有碰撞器的对象存在,不然,射线射谁呢?

测试代码

using UnityEngine;
using System.Collections;
//游戏控制脚本,可以添加到创建的空物体身上
public class GameController : MonoBehaviour
{   
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //从摄像机发出一条射线
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            //是否碰撞到带有碰撞器的对象
            if (Physics.Raycast(ray, out hit, Mathf.Infinity))
            {       
                     //从对象池中提取对象     
                    GameObject currentObj = ObjectPoolSingleton.Instance.OutPool("Sphere",
                        Camera.main.transform.position, Quaternion.identity);
                    //球对象的速度要初始化为0,不然,会在原来基础上再次添加力
                    currentObj.GetComponent().velocity = Vector3.zero;
                   //给对象一个力
                    currentObj.GetComponent().AddForce(ray.direction*1200);
                }
            }
        }
    }
}

该脚本挂载到球预设体身上(注意:球要添加刚体组件,需要添加力,并且该预设体要放在Resources文件夹下,需要动态加载,名称要与你提取对象所传入的字符串名称相同)

using UnityEngine;
using System.Collections;
//该脚本挂载到球预设体身上(球要添加刚体组件,需要添加力)
public class Bullet : MonoBehaviour {
//注意:必须OnEnable回调函数里,此函数在脚本生命周期里是可以循环调用的,对象SetActive(true)
//时,就会调用
    void OnEnable()
    {
        StartCoroutine("Join");
    }
    IEnumerator Join()
    {
        yield return new WaitForSeconds(2.5f);
        //延时2.5S后进对象池,你就看不见了
        ObjectPoolSingleton.Instance.JoinPool(gameObject);
    }
}

写在最后

    ###代码虐我千百遍,催我快点找初恋###

你可能感兴趣的:(Unity——对象池)