先举一个射击游戏例子:
玩家不断的射击,不断的产生射击特效,弹痕,子弹,弹壳等各种物体,这些物体一段时间后自动销毁,但unity Destory后并没有释放其内存,不断堆积后内存爆满,GC频繁,游戏性能降低,所以我们需要降低GC的频率,因此就使用了缓存池来保留这些马上就会复用的资源,而不是直接销毁,GC频率降低,游戏体验提升。
不严谨的再举个栗子:
内存容量为50,每秒射击一次产生的各种物体要占用内存1单位,1s后其物体自动销毁或保存到缓存池里,我们计算220s内,每秒只射击一次的GC次数(只计算因为堆内存满后的GC)
- 不使用缓存池:GC次数大概为4次
- 使用缓存池 :GC次数为0次
好处就很显然了,一句话概括就是用内存换取游戏性能。
上面引用了缓存池的概念,那么缓存池是什么?
缓存池是什么
一个机制一个模块,管他娘的是什么🤡🤡🤡🤡。知道怎么用,为什么用后,自己就可以用自己的话去形容它了。
我的小偷理解就是:缓存池就是衣柜,想穿就往里面拿,里面没有就去买,买了直接穿,穿完再放回,衣柜还得有个容量,上到无穷大,衣柜炸了别找我,下到啥都不准放,那你还要柜子干啥,你觉得多大就是多大,不要我觉得要你觉得,衣柜满了就丢一些不要的衣服,想丢什么丢什么,最好先把要穿的衣服都准备好,别到时候要穿了还没有
大概就是这些。
搭建缓存池
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
private Dictionary<string, Queue<GameObject>> objectPool = new Dictionary<string, Queue<GameObject>>(); 字典就是衣柜 字典值就时衣柜里的抽屉,用来区分不同的衣服 字典键就时不同抽屉的标签,这里直接用存放的物体名字来区分 ------- 容量是为了限制缓存池所占的内存大小
private int? poolCapacity=null;
private int? queueCapacity = null;
|
功能如上,具体用什么数据结构储存看自己的需求
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
public void PreLoad(string name,int preNum) { Queue<GameObject> gameObjects = new Queue<GameObject>(); for(int i=0;i<preNum;i++) { gameObjects.Enqueue(Resources.Load<GameObject>(name)); } }
|
取物体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public GameObject GetObject(string name) { GameObject obj = null; if(objectPool.ContainsKey(name)&&objectPool[name].Count>0) { obj = objectPool[name].Dequeue(); obj.SetActive(true); } else { obj = GameObject.Instantiate( Resources.Load<GameObject>(name)); obj.name = name; } return obj; }
|
存物体
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
|
public void ReturnObject(string name,GameObject obj) { obj.SetActive(false); if (objectPool.ContainsKey(name)) { if (objectPool[name].Drawer.Count < queueCapacity) objectPool[name].ReturnObject(obj); else GameObject.Destroy(obj); } else if(objectPool.Count>=poolCapacity) { GameObject.Destroy(obj); } else { Queue<GameObject> objQueue = new Queue<GameObject>(); objQueue.Enqueue(obj); objectPool.Add(name, objQueue); } }
|
清空
1 2 3 4 5 6 7
|
public void Clear() { objectPool.Clear(); }
|
效果如图

但还不满意,如果需要缓存的对象很多,则这样在Hierarchy里就会显得非常的乱,那就别叫缓存池了,就叫做茅厕,poo pee fart混杂,所以需要将缓存池在Hierarchy具象出来,同时把抽屉抽象成一个类,设置父子对象,让缓存池在Hierarchy结构更加清晰。
效果如下

完整代码
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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class Drawers { private GameObject father; private Queue<GameObject> drawer; public Queue<GameObject> Drawer { get { return drawer; } } public Drawers(GameObject obj,Transform wardrobe) { father = new GameObject(obj.name+"'s"+"屉子"); father.transform.SetParent(wardrobe); obj.transform.SetParent(father.transform); drawer = new Queue<GameObject>() ; drawer.Enqueue(obj); } public GameObject GetObject() { drawer.Peek().SetActive(true); return drawer.Dequeue(); }
public void ReturnObject(GameObject obj) { drawer.Enqueue(obj); obj.transform.SetParent(father.transform); } }
public class Wardrobe : BasicManager<Wardrobe> { private Dictionary<string, Drawers> objectPool = new Dictionary<string,Drawers>(); public GameObject wardrobe; private int? poolCapacity=10; private int? queueCapacity = 50; public GameObject GetObject(string name) { if(objectPool.ContainsKey(name)&&objectPool[name].Drawer.Count>0) { return objectPool[name].GetObject(); } else { GameObject obj = GameObject.Instantiate(Resources.Load<GameObject>(name)); obj.name = name; return obj; } }
public void ReturnObject(string name,GameObject obj) { obj.SetActive(false); if (wardrobe == null) wardrobe = new GameObject("衣柜"); if (objectPool.ContainsKey(name)) { if (objectPool[name].Drawer.Count < queueCapacity) objectPool[name].ReturnObject(obj); else GameObject.Destroy(obj); } else if(objectPool.Count>=poolCapacity) { GameObject.Destroy(obj); } else { Drawers draw = new Drawers(obj, wardrobe.transform); objectPool.Add(name, draw); } } public void PreLoad(string name,int preNum) { wardrobe = new GameObject("衣柜"); Queue<GameObject> gameObjects = new Queue<GameObject>(); for(int i=0;i<preNum;i++) { gameObjects.Enqueue(Resources.Load<GameObject>(name)); } }
public void Clear() { objectPool.Clear(); GameObject.Destroy(wardrobe); } }
|