1. 程式人生 > >Unity簡單物件緩衝池技術

Unity簡單物件緩衝池技術

一、建立物件緩衝池指令碼

/***
 *
 *  Title: 
 *         預載入與物件緩衝池技術
 *         
 *                  物件緩衝池管理器        
 * 
 *  Description:
 *        基本原理:
 *        通過池管理思路,在遊戲初始化的時候,生成一個初始的池存放我們要複用的元素。
 *        當要用到遊戲物件時,從池中取出;不再需要的時候,不直接刪除物件而是把物件重新回收到“池”中。
 *        這樣就避免了對記憶體中大量物件的反覆例項化與回收垃圾處理,提高了資源的利用率。      
 *
 *  Date: 2018
 * 
 *  Version: 1.0
 *
 *  Modify Recorder:
 *     
 */
using UnityEngine;
using System.Collections.Generic;

public class ObjectPoolManager : MonoBehaviour{
        public GameObject ObjPrefab;                                           //池中所使用的元素預設
        public Transform TranObjPrefabParent;                                  //池中所使用的元素預設的父物件
        public int InitialCapacity;                                            //初始容量

        private int _startCapacityIndex;                                       //初始下標
        private List<int> _avaliableIndex;                                     //可用“池”遊戲物件下標
        private Dictionary<int, GameObject> _totalObjList;                     //池中全部元素的容器


        /// <summary>
        /// 初始化緩衝池
        /// </summary>
        void Awake(){
            _avaliableIndex = new List<int>(InitialCapacity);
            _totalObjList = new Dictionary<int, GameObject>(InitialCapacity);
            //初始化池
            expandPool();
        }

        /// <summary>
        /// 取得遊戲物件。
        /// </summary>
        /// <returns></returns>
        public KeyValuePair<int, GameObject> PickObj(){
            //容量不夠,進行“池”擴充套件
            if (_avaliableIndex.Count == 0)
                expandPool();
            //取得一個可用的池下標數值
            int id = _avaliableIndex[0];
            //“可用池下標”集合,刪除對應下標
            _avaliableIndex.Remove(id);
            //設定“池”物件可用。
            _totalObjList[id].SetActive(true);
            //從“池”中提取一個物件返回。
            return new KeyValuePair<int, GameObject>(id, _totalObjList[id]);
        }

        /// <summary>
        /// 回收遊戲物件
        /// </summary>
        /// <param name="id"></param>
        public void RecyleObj(int id) {
            //設定對應物件不可用(即:放回池操作)
            _totalObjList[id].SetActive(false);
            //指定Id的遊戲物件下標,重行進入可用“池”下標集合中
            _avaliableIndex.Add(id);
        }

        /// <summary>
        /// 擴充套件池
        /// </summary>
        private void expandPool(){
            int start = _startCapacityIndex;
            int end = _startCapacityIndex + InitialCapacity;

            for (int i = start; i < end; i++){
                //加入驗證判斷,避免在多個請求同時觸發擴充套件池需求
                if (_totalObjList.ContainsKey(i))
                continue;
                GameObject newObj = Instantiate(ObjPrefab) as GameObject;
                //生成的池物件增加父物件,容易檢視與檢查。
                newObj.transform.parent = TranObjPrefabParent.transform;
                //每一個生成的物件,設定暫時不可用。
                newObj.SetActive(false);
                //下標記入“池”可用下標集合中。
                _avaliableIndex.Add(i);
                //新產生的物件,併入本池容器集合中。
                _totalObjList.Add(i, newObj);
            }
            //擴充套件“初始下標”。
            _startCapacityIndex = end;
        }
    }//Class_end

二、給池中的預設物件添加回收指令碼

/***
 *
 *  Title: 
 *         預載入與物件緩衝池技術
 *
 *  Description:
 *        功能:
 *            回收物件指令碼
 *
 *  Date: 2018
 * 
 *  Version: 1.0
 *
 *  Modify Recorder:
 *     
 */

using UnityEngine;
using System.Collections;

public class DestroyObjUseBufferPool : MonoBehaviour 
{
    public GameObject GoPoolManager;                       //池管理器
    private ObjectPoolManager _PoolManagerObj;             //物件池管理器
    private int _IntBulletID = 0;                          //子彈ID編號 

    void Start()
    {
        _PoolManagerObj = GoPoolManager.GetComponent<ObjectPoolManager>();
    }

    /// <summary>
    /// 遊戲物件超出攝像機可視範圍,則此物件進行“回收”。
    /// </summary>
    void OnBecameInvisible()
    {
        _PoolManagerObj.RecyleObj(_IntBulletID);
    }

    /// <summary>
    /// 接收本指令碼所屬物件的ID編號,用於回收使用。
    /// </summary>
    /// <param name="intBulleteNumber"></param>
    public void ReceiveBulletID(int intBulleteNumber)
    {
        _IntBulletID = intBulleteNumber;
    }
}

三、編寫好物件緩衝池的內容之後就需要配合專案使用(以射擊專案為例)

/***
 *
 *  Title: 
 *         預載入與物件緩衝池技術
 *
 *  Description:
 *        功能:
 *            學習“物件緩衝池”技術
 *            利用緩衝池實現射擊程式碼
 *
 *  Date: 2018
 * 
 *  Version: 1.0
 *
 *  Modify Recorder:
 *     
 */
using UnityEngine;
using System.Collections;
using System.Collections.Generic;   

public class ShottingUseBufferPool : MonoBehaviour{
    public Texture Texture_ShootingCursor;                 //射擊瞄準星
    public GameObject G0_CubeOrigianl;                     //射擊原型物體
    public Transform Tran_TargetWallParentPosition;        //靶牆陣列父物件
    public Transform Tran_BulletParentPosition;            //子彈陣列父物件
    private Vector3 _VecRayPosion;                         //射線透射的座標

    public GameObject GoPoolManager;                       //池管理器  
    public GameObject GoBulletPrefabsOriginal;             //子彈原型(預設) 
    private ObjectPoolManager boPoolManager;               //池管理器物件
    private GameObject goCloneBullete;                     //克隆的子彈

    /// <summary>
    /// 初始化場景
    /// </summary>
    void Start(){
        //隱藏滑鼠。
        Cursor.visible = false;

        //取得池管理器
        boPoolManager = GoPoolManager.GetComponent<ObjectPoolManager>();     

        //建立射擊目標靶牆
        for (int j = 1; j <= 5; j++)
        {
            for (int i = 1; i <= 5; i++)
            {
                GameObject goClone = (GameObject)Instantiate(G0_CubeOrigianl);
                goClone.transform.position = new Vector3(G0_CubeOrigianl.transform.position.x + i,
                    G0_CubeOrigianl.transform.position.y + j, G0_CubeOrigianl.transform.position.z);
                //確定子彈的父物件
                goClone.transform.parent = Tran_TargetWallParentPosition;
            }
        }
    }//Start_end

    /// <summary>
    /// 繪製射擊游標
    /// </summary>
    void OnGUI(){
        Vector3 vecPos = Input.mousePosition;
        GUI.DrawTexture(new Rect(vecPos.x - Texture_ShootingCursor.width / 2,
            Screen.height - vecPos.y - Texture_ShootingCursor.height / 2, Texture_ShootingCursor.width,
            Texture_ShootingCursor.height), Texture_ShootingCursor);
    }//OnGUI_end

    /// <summary>
    /// 射擊邏輯處理
    /// </summary>
    void Update(){
        //射線處理
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit))
        {
            //獲取射線碰撞到碰撞體的方位
            _VecRayPosion = hit.point;
        }

        //如果滑鼠點選左鍵,則發射子彈。
        if (Input.GetMouseButtonDown(0)){
            //建立子彈
            KeyValuePair<int, GameObject> kvObj = boPoolManager.PickObj();
            if (kvObj.Value != null){
                goCloneBullete = kvObj.Value;
                goCloneBullete.SendMessage("ReceiveBulletID", kvObj.Key);
            }
            //新增子彈剛體
            if (!goCloneBullete.GetComponent<Rigidbody>()){
                goCloneBullete.AddComponent<Rigidbody>();
            }
            //子彈的位置
            goCloneBullete.transform.position = new Vector3(Camera.main.transform.position.x, Camera.main.transform.position.y, Camera.main.transform.position.z + 0.3F);
            //給子彈加“力”
            goCloneBullete.GetComponent<Rigidbody>().AddForce((_VecRayPosion - goCloneBullete.transform.position) * 10F, ForceMode.Impulse);  
        }
    }//Update_end  

}//Class_end

注意:該內容來自《Unity3D/2D遊戲開發從0到1》27章