空池触发和借用模式 空池触发,顾名思义就是在对象池空的时候获取对象会直接去生成,首先上一个简单的空池触发和借用模式的对象池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public sealed class GameObjectPool : MonoBehaviour { public static GameObjectPool Instance { get ; private set ; } private Dictionary<string , List<GameObject>> pool = new Dictionary<string , List<GameObject>>(); private void Awake () { if (Instance == null ) { Instance = this ; } else { Destroy(gameObject); } } public GameObject Get (string prefabName, Vector3 position, Quaternion rotation ) { var key = prefabName; GameObject obj; if (pool.ContainsKey(key) && pool[key].Count > 0 ) { var list = pool[key]; obj = list[0 ]; list.RemoveAt(0 ); obj.SetActive(true ); obj.transform.position = position; obj.transform.rotation = rotation; } else { obj = Instantiate(Resources.Load(prefabName), position, rotation, transform) as GameObject; } return obj; } public void Store (GameObject obj ) { var key = obj.name.Replace("(Clone)" , string .Empty); if (pool.ContainsKey(key)) { pool[key].Add(obj); } else { pool[key] = new List<GameObject>() { obj }; } obj.SetActive(false ); } }
此为一个很简单也很经典的对象池实现,很容易我们就可以发现一个问题:
如果我对象获取的频率太高了获取还能连贯吗?
很明显,不能。当某次对象获取请求太快。前面一部分可能是正常获取池中已有物品,但是后面空池的时候,会阻断这次获取,改为先去执行生成。生成本身就是一个耗时的操作,这必然会导致我们的获取不连贯,导致卡顿。
水位线 为了优化空池触发的性能,此处我们引入水位线模式的对象池。和空池类似,只不过水位线加入了一个触发分配的阈值,比如池子里面还剩10个物体的时候就要开始分配了。此时,分配过程 和 get
相互独立
同时我们需要明确一下空池触发的缺点:即 get
的时候现场生成导致 get
时间变成,而空池补充这个操作,我们就得放在非 get
时机,也就是生成和 get
分离
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 public sealed class GameObjectPool : MonoBehaviour { public static GameObjectPool Instance { get ; private set ; } private Dictionary<string , List<GameObject>> pool = new Dictionary<string , List<GameObject>>(); public int WaterLevel { get ; private set ; } = 10 ; private bool isNeedGenerate = false ; private string generateName; private void Awake () { if (Instance == null ) { Instance = this ; } else { Destroy(gameObject); } } public GameObject Get (string prefabName, Vector3 position, Quaternion rotation ) { var key = prefabName; if (!pool.ContainsKey(key)) { pool[key] = new List<GameObject>(); InitializeToWaterLevel(key); } GameObject obj; if (pool[key].Count > 0 ) { var list = pool[key]; obj = list[0 ]; list.RemoveAt(0 ); obj.SetActive(true ); obj.transform.position = position; obj.transform.rotation = rotation; if (pool[key].Count < WaterLevel) { isNeedGenerate = true ; generateName = prefabName; } } else { Debug.Log("水位线可能较低" ); obj = Instantiate(Resources.Load(key), position, rotation, transform) as GameObject; } return obj; } public void Store (GameObject obj ) { var key = obj.name.Replace("(Clone)" , string .Empty); if (pool.ContainsKey(key)) { pool[key].Add(obj); } else { pool[key] = new List<GameObject>() { obj }; } obj.SetActive(false ); } private void InitializeToWaterLevel (string key ) { GenerateToPool(key, WaterLevel); } private void GenerateToWaterLevel (string key ) { var currCount = pool[key].Count; GenerateToPool(key, WaterLevel - currCount); } private void GenerateToPool (string key, int num ) { for (int i = 0 ; i < num; i++) { var obj = Instantiate(Resources.Load(key), transform) as GameObject; obj.SetActive(false ); pool[key].Add(obj); } } private void Update () { if (isNeedGenerate) { GenerateToWaterLevel(generateName); isNeedGenerate = false ; } } }
此处我实现了一个较为简单的水位线对象池,在 get
时对不足水位线的物体进行标记,然后在 update
里面轮询标记来生成缺少的物体。我没有在 get
里面做一个 Action
之类的东西,因为感觉在返回之前这样还是会导致 get
在生成物体时阻塞掉,以至于 get
速度变慢
TODO:lease/return 对象池 和 借用模式和引用计数模式