使用對象池優化性能

一:前言

在遊戲的製做過程當中,咱們可能會碰到這樣的狀況,開槍的時候射出子彈,每一個子彈即一個對象,正常狀況,咱們的處理方式可能會是每開一槍就Instantiate一個新的子彈,當子彈到達極限距離或者碰到物體後再Destroy銷燬它。假設有射出1000發子彈,咱們就會執行1000次這樣的操做,反覆建立銷燬是一個內存反覆分配與釋放的過程,很容易產生內存碎片。在Unity中Instantiate和Destroy操做,不只影響性能還容易產生內存碎片,在Unity中建立或是銷燬對象須要付出昂貴的代價的。而用對象池能夠解決性能開銷問題ide

內存碎片:
內存碎片的意思是內存被分紅一個一個的小塊而不是整個大塊,全部內存小塊的大小可能很大但並不能使用,好比你想分配16byte的內存,此時若是有 20byte的空間就能夠分配成功,可是若是這20byte是內存碎片,爲兩個10byte就會分配失敗。因此,若是存在大量內存碎片,理論上有足夠的可用內存,也會分配失敗
不少遊戲公司的遊戲都會進行浸泡測試,讓一個遊戲跑好幾天,查看是否崩潰來檢測內存泄露等等,由於內存碎片產生毀滅性的結果是一個緩慢的過程性能


二:什麼是對象池?

池能夠理解爲咱們現實生活中的游泳池,裏面裝滿了水,而在計算機世界裏稱爲必定數量水的集合。因此池的概念和集合很類似。例如內存池就是必定數量的已經分配好的內存的集合。線程池就是必定數量的已經建立好的線程的集合,那麼,對象池,顧名思義就是必定數量的已經建立好的對象(Object)的集合
對象池的原理就是預先分配一大塊內存,生成滿須要常常用的對象,而後直到不使用再所有釋放測試


三:對象池的優勢

對象池就是複用池中對象,沒有分配內存和建立堆中對象的開銷,進而減小垃圾收集器的負擔, 避免內存抖動this


四:對象池的應用場景

在製做過程當中當常常有同一個Prefab要用到屢次,須要反覆實例化(Instantiate)和銷燬(Desroy)。例如射擊遊戲中的子彈,跑酷遊戲中的障礙物,路徑等.....
 線程

五:對象池的使用

在遊戲加載時把一批Prefab實例化好放在對象池中,遊戲中用的時候拿出來(SetActive=true),不用的時候放回去(SetActive=false),避免反覆實例化和銷燬。能夠理解爲對象池就是一個租借處,須要的時候借出去,用完了再還回來code


六:對象池的使用流程

——————————————————————ReusableObject腳本orm

using UnityEngine;

/// <summary>
/// 對象池中的每一個物體都須要重寫取出和放回的方法
/// </summary>
public abstract class ReusableObject : MonoBehaviour
{
    /// <summary>
    /// 取出時
    /// </summary>
    public abstract void OnSpawn();

    /// <summary>
    /// 回收時
    /// </summary>
    public abstract void OnUnSpawn();
}


——————————————————————SubPool腳本 對象

using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// 每一個子池子
/// </summary>
public class SubPool
{
    //預製體
    private GameObject prefab;
    //父物體
    private Transform parent;

    //當前子池子中的全部物體
    private List<GameObject> goList = new List<GameObject>();

    /// <summary>
    /// 初始化當前子池子
    /// </summary>
    /// <param name="prefab">預製體</param>
    /// <param name="parent">父物體</param>
    public SubPool(GameObject prefab, Transform parent)
    {
        this.prefab = prefab;
        this.parent = parent;
    }

    #region public方法

    /// <summary>
    /// 取出物體
    /// </summary>
    public GameObject Spawn()
    {
        GameObject go = null;

        go = GetGoFromList();

        if (go == null)
        {
            go = GameObject.Instantiate(prefab, parent);
            goList.Add(go);
        }

        go.GetComponent<ReusableObject>().OnSpawn();//取出物體時執行的方法
        go.SetActive(true);
        return go;
    }

    /// <summary>
    /// 回收物體
    /// </summary>
    /// <param name="go">要回收的物體</param>
    public void UnSpawn(GameObject go)
    {
        go.GetComponent<ReusableObject>().OnUnSpawn();//回收物體時執行的方法
        go.SetActive(false);
    }

    /// <summary>
    /// 回收全部物體
    /// </summary>
    public void UnSpawnAll()
    {
        foreach (var temp in goList)
        {
            if (temp.activeSelf)
            {
                UnSpawn(temp);
            }
        }
    }

    /// <summary>
    /// 當前子池子中是否包含此遊戲物體
    /// </summary>
    /// <param name="go">判斷的遊戲物體</param>
    public bool Contains(GameObject go)
    {
        return goList.Contains(go);
    }

    #endregion

    #region private方法

    /// <summary>
    /// 從goList中獲得一個物體
    /// </summary>
    /// <returns></returns>
    private GameObject GetGoFromList()
    {
        GameObject go = null;

        foreach (var temp in goList)
        {
            if (temp.activeSelf == false)
            {
                go = temp;
            }
        }

        return go;
    }

    #endregion
}


 

——————————————————————ObjectPool腳本 遊戲

using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// 對象池總管理器
/// </summary>
public class ObjectPool : MonoBehaviour
{
    //單例
    public static ObjectPool Instance { get; set; }

    //每一個對象池物體信息的字典
    private Dictionary<string, Transform> poolInfoDict = new Dictionary<string, Transform>();
    //每一個子池子的字典
    private Dictionary<string, SubPool> subPoolDict = new Dictionary<string, SubPool>();

    [Header("資源目錄")]
    public string resDir;
    [Header("手動添加全部對象池物體")]
    [Space(25)]
    public ObjectInfo[] objects;

    private void Awake()
    {
        Instance = this;

        //初始化每一個對象池物體信息的字典
        foreach (var o in objects)
        {
            if (!poolInfoDict.ContainsKey(o.resName))
            {
                poolInfoDict.Add(o.resName, o.parent);
            }
        }
    }

    #region main

    /// <summary>
    /// 取出物體
    /// </summary>
    /// <param name="resName">資源名稱</param>
    public GameObject Spawn(string resName)
    {
        GameObject go = null;

        if (!subPoolDict.ContainsKey(resName))
        {
            if (!CreatePool(resName))
            {
                Debug.LogWarning("建立池子失敗:" + resName);
                return go;
            }
        }

        SubPool pool = subPoolDict[resName];
        go = pool.Spawn();//取出物體
        return go;
    }

    /// <summary>
    /// 回收物體
    /// </summary>
    /// <param name="go">要回收的物體</param>
    public void UnSpawn(GameObject go)
    {
        foreach (var pool in subPoolDict.Values)
        {
            if (pool.Contains(go))
            {
                pool.UnSpawn(go);
                break;
            }
        }
    }

    /// <summary>
    /// 回收全部物體
    /// </summary>
    public void UnSpawnAll()
    {
        foreach (var pool in subPoolDict.Values)
        {
            pool.UnSpawnAll();
        }
    }

    #endregion

    #region private方法

    /// <summary>
    /// 建立池子
    /// </summary>
    /// <param name="resName">資源名稱</param>
    private bool CreatePool(string resName)
    {
        if (!poolInfoDict.ContainsKey(resName))
        {
            Debug.LogWarning("不存在此子池子:" + resName + ",請在面板中手動添加資源名稱");
            return false;
        }

        string path = resDir + resName;
        GameObject prefab = Resources.Load<GameObject>(path);
        if (prefab == null)
        {
            Debug.LogWarning("路徑不正確:" + path);
            return false;
        }
        SubPool pool = new SubPool(prefab, poolInfoDict[resName]);
        subPoolDict.Add(resName, pool);

        return true;
    }

    #endregion
}

/// <summary>
/// 每一個對象池中的物體信息
/// </summary>
[System.Serializable]
public class ObjectInfo
{
    //資源名稱
    public string resName;
    //父物體
    public Transform parent;
}
相關文章
相關標籤/搜索