建议阅读缩放比例:150%
对象池是什么?
很明显,就是一个用来装对象的大池子,通常我们称拥有这种池子的人为海王(不是)
在游戏运行中,经常会出现大量重复的物体,我们需要不断的去生成和删除这些对象,但这么做会导致我们的内存空间产生碎片,使得存储空间不连续,进而导致触发 GC (垃圾回收机制),造成游戏卡顿。
为了避免这种情况,我们可以创建一个管理这些大量重复物体的管理中心,我们可以把这个管理中心看做是一个仓库,在仓库中一开始有已经有一些货物,但货物在仓库中,我们看不见,当我们需要用的时候,再从仓库中取出,使货物变得可见,等我们用完货物之后再还给仓库,再次变得不可见,而有的时候,仓库里的库存已经不能满足我们的需求了,这时我们需要仓库进货,进新的货物供我们使用,再返还时,仓库的库存可能太多了,塞不下,短时间内我们也不会用到这么多,仓库便会自己再处理掉货物。
这个仓库,就是所谓的对象池。
很多时候,我们都需要是要对象池来对我们的游戏进行优化,以减少内存的负担,射击游戏中的子弹便是最常见的对象池使用案例,大量重复生成的子弹可以重复利用。
如何设计对象池?
容器的选择
为了创建对象池,我们首先需要一个字典,来让我们需要的对象和池子以键值对(key - value)的方式进行绑定,key 的类型取决于你的需要,value 的选择通常有两种方式,一个是使用队列(Queue),一个是使用列表(list),本文中使用队列方式。
Dictionary<GameObject, Queue<GameObject>>
从池中取出对象
ok,我们现在已经有了一个池子,接下来我们就可以获取对象了,使用队列的 Dequeue 方法,即便我们还没有放入对象,我们可以判定当库存为空时,就创建一个新的对象并返回。我这里因为获取对象需要初始化位置与方向信息,所以参数使用了 Vector3 和 Quaternion 。
public GameObject GetObject(GameObject prefab, Vector3 position, Quaternion rotation) {
if (!pool.ContainsKey(prefab)) {
pool.Add(prefab, new Queue<GameObject>());
}
if (pool[prefab].Count == 0) {
GameObject instance = Object.Instantiate(prefab, position, rotation);
return instance;
}
GameObject newObject = pool[prefab].Dequeue();
newObject.SetActive(true);
newObject.transform.position = position;
newObject.transform.rotation = rotation;
return newObject;
}
将对象放入池子
当我们需要放回对象时,使用队列的 Enqueue 方法,值得一提的是,最好设置一个仓库最大值,不然仓库可能会储存过多的对象
public void RecycleObject(GameObject prefab, GameObject gameObject) {
if (pool[prefab].Count >= maxCount) {
Object.Destroy(gameObject);
} else {
gameObject.SetActive(false);
pool[prefab].Enqueue(gameObject);
}
}
预加载对象
我们可以在池子中预加载好一定量的对象,以备之后使用
public class ObjectPool {
public int maxCount = 128;
public static ObjectPool instance = new ObjectPool();
public Dictionary<GameObject, Queue<GameObject>> pool = new Dictionary<GameObject, Queue<GameObject>>();
代码总体
// 从池中取出物体
public GameObject GetObject(GameObject prefab, Vector3 position, Quaternion rotation) {
if (!pool.ContainsKey(prefab)) {
pool.Add(prefab, new Queue<GameObject>());
}
if (pool[prefab].Count == 0) {
GameObject instance = Object.Instantiate(prefab, position, rotation);
return instance;
}
GameObject newObject = pool[prefab].Dequeue();
newObject.SetActive(true);
newObject.transform.position = position;
newObject.transform.rotation = rotation;
return newObject;
}
// 将物体放回池子
public void RecycleObject(GameObject prefab, GameObject gameObject) {
if (pool[prefab].Count >= maxCount) {
Object.Destroy(gameObject);
} else {
gameObject.SetActive(false);
pool[prefab].Enqueue(gameObject);
}
}
// 预加载物体
public void Init(GameObject prefab, int num) {
if (!pool.ContainsKey(prefab)) {
pool.Add(prefab, new Queue<GameObject>());
}
for (int i = 0; i < num; i++) {
GameObject newObject = Object.Instantiate(prefab);
newObject.SetActive(false);
pool[prefab].Enqueue(newObject);
}
}}