在遊戲的製做過程當中,咱們可能會碰到這樣的狀況,開槍的時候射出子彈,每一個子彈即一個對象,正常狀況,咱們的處理方式可能會是每開一槍就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; }