1. 程式人生 > >淺談物件池技術在遊戲中的應用

淺談物件池技術在遊戲中的應用

說到做遊戲,遊戲的優化就至關重要,今天所說的就是關於遊戲優化的一種方式——物件池技術。

物件池技術的主要應用於遊戲中反覆利用的遊戲資源(比如:子彈,技能特效,怪物等等)

物件池的具體思想:

將使用過的資源(會反覆利用的)儲存到池子中,下次需要的時候直接去池子中取出來。

第一步:構建物件池

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 物件池 所有池子都在字典裡 字典裡存的是單個池子的List
/// </summary>
public class GameObjectPool: MonoBehaviour
{
    /// <summary>
    /// 單利
    /// </summary>
    private static GameObjectPool instance;
    /// <summary>
    /// 獲得單利
    /// </summary>
    public static GameObjectPool Instance
    {
        get
        {
            if (!instance)
            {
                instance = new GameObject("_Pool").AddComponent<GameObjectPool>();
                //設定在場景載入中不銷燬物件池
                DontDestroyOnLoad(instance.gameObject);
            } 
            return instance;
        }
    }
    /// <summary>
    /// 所有池子的字典 
    /// </summary>
    Dictionary<string, List<GameObject>> _PoolDic=new Dictionary<string, List<GameObject>>();
    /// <summary>
    /// 獲得池子中的物件
    /// </summary>
    /// <param name="name">物件的名字</param>
    /// <returns></returns>
    public GameObject GetObject(string name)
    {
        //定義一個儲存物件的容器
        GameObject Object;
        //有這個名字的池子 
        if (_PoolDic.ContainsKey(name))
        {
            //並且池子中有物件
            if (_PoolDic[name].Count > 0)
            {
                Object = _PoolDic[name][0];
                _PoolDic[name].Remove(_PoolDic[name][0]);
            }
            else //並且池子中沒有物件
            {
                Object = GameObject.Instantiate(ResourcesManager.Instance._PrefabsDic[name]);
                Object.transform.SetParent(transform);
            }
        }
        else//沒有這個池子
        {
            //建立池子 例項物件
            _PoolDic.Add(name, new List<GameObject>());
            Object = GameObject.Instantiate(ResourcesManager.Instance._PrefabsDic[name]/*這是一個工具類,用於載入物件資源*/);
            Object.transform.SetParent(transform);
        }
        //更改物件的名字
        Object.name = name;
        return Object;
    }
    /// <summary>
    /// 往池子中新增物件
    /// </summary>
    /// <param name="Object"></param>
    public void SetObject(GameObject go)
    {
        go.SetActive(false);
        //字典中有這個池子
        if (_PoolDic.ContainsKey(go.name))
        {
            _PoolDic[go.name].Add(go);
        }
        else //字典中沒有這個池子
        {
            //建立新的池子
            _PoolDic.Add(go.name,new List<GameObject>());
            //將物件加入池子
            _PoolDic[go.name].Add(go);
        }
    }
}

第二部 構建物件池 所需要的資源載入工具 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 載入資源用的
/// </summary>
public class ResourcesManager : MonoBehaviour {
    /// <summary>
    /// 單利的儲存欄位
    /// </summary>
    private static ResourcesManager instance;
    /// <summary>
    /// 單利的屬性用於獲取
    /// </summary>
    public static ResourcesManager Instance
    {
        get
        {
            if (!instance)
                instance= new GameObject("_ResourcesManager").AddComponent<ResourcesManager>();
            return instance;
        }
    }
    /// <summary>
    /// 儲存所載入的遊戲物件
    /// </summary>
    public Dictionary<string, GameObject> _PrefabsDic=new Dictionary<string, GameObject>();
    void Awake () {
        _PrefabsDic.Add("Cube", Resources.Load<GameObject>("Cube"));
        _PrefabsDic.Add("Bullet", Resources.Load<GameObject>("Bullet"));
    }
    //TODO: 這裡可以直接寫一個根據名字直接載入的方法
    /// <summary>
    /// 根據名字獲取資源 有就直接返回 沒有去載入
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public GameObject LoadGameObjectByName(string name)
    {
        //安全校驗
        if (_PrefabsDic.ContainsKey(name)) return _PrefabsDic[name];

        _PrefabsDic.Add(name, Resources.Load<GameObject>(name));
        return _PrefabsDic[name];
    }
}
第三步:回收到物件池中的指令碼
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 掛在物件上 用於物件的入池
/// </summary>
public class SetPool : MonoBehaviour {
    public float time=3f;
    private void OnEnable()
    {//延時呼叫函式
        Invoke("SetPoolObject", time);
        Debug.Log(1);
    }
    public void SetPoolObject()
    {
        GameObjectPool.Instance.SetObject(gameObject);
        Debug.Log(2);
    }
}
掛載在遊戲物件身上
注意:這裡要回收到物件池身上 要對物件進行初始化處理 也就是恢復到加入池子的狀態

下面是遊戲中的使用案例:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameController : MonoBehaviour {
    //這裡用箱子模擬敵人 球模擬子彈
    /// <summary>
    /// 生成箱子的行數
    /// </summary>
    public int Row = 5;
    /// <summary>
    /// 生成箱子的列數
    /// </summary>
    public int Line = 10;
    /// <summary>
    /// 生成物體的位置X方向的偏移
    /// </summary>
    public float Offset=-5f;
    /// <summary>
    /// 子彈的速度
    /// </summary>
    public float speed=3f;
    /// <summary>
    /// 射線
    /// </summary>
    private RaycastHit hit;
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out hit))
            {
                //具體的引用
                GameObject bullet = GameObjectPool.Instance.GetObject("Bullet");
                bullet.SetActive(true);
                bullet.transform.position = Camera.main.transform.position;
                bullet.GetComponent<Rigidbody>().velocity = (hit.collider.gameObject.transform.position - Camera.main.transform.position).normalized * speed * Time.deltaTime;
            }

        }
    }
    void Start () {
        //生成箱子(敵人) 場景初始化
        for (int i = 0; i < Row; i++)
        {
            for (int j = 0; j < Line; j++)
            {
               GameObject game= GameObject.Instantiate(ResourcesManager.Instance._PrefabsDic["Cube"]);
                game.transform.SetParent(transform);
                game.transform.position = new Vector3(Offset + j, 0.5f + i, 0);
            }
        }
	}
}