原文地址:https://app.gitbook.com/@galaxyzeta/s/unity3d/shi-xian-unity3d-dui-xiang-chi
为何需要对象池?
想像你的游戏中有一架飞机,它每秒钟射出 1000 个相同的子弹对象,但他们的存活时间都只有1s。如果引擎每秒钟新增,删除1000次同样的物体,对性能将会产生较大影响。
解决这个问题的一种办法就是引入池化技术:
- 游戏开始前,将这些子弹全部存放到一个池子中;
- 当我们需要这些物体的时候,直接拿出来初始化一下位置、属性等,就可以继续用;
- 当我们需要从场景中移除某些被池化的对象,只需要将这些对象设置为 Inactive 即可,而不是将其删除。
池子中的对象永远就那么几个,场景中的对象并没有真正消失,它们只是暂时被 Inactivate 了。这一波操作下来,引擎无需反复执行创建、删除操作,性能就大大提升了。
对象池技术事实上就是设计模式中的享元模式(Flyweight Pattern)。
核心思想
- 使用
List<GameObject> 数据结构管理对象。 - 提供
GetReferenceFromPool 方法,方法有两种重载:
- 无参数,直接拿第 0 号对象。
- 有参数,拿 index 位置的对象。
- 当我们需要从 pool 中拿指定 index 的对象时:
- 如果 pool 中还没有对象,则创建对象,并返回给用户一个 active 的 gameObject。
- 如果 pool 中已经有对象:
- 需要先检查这个对象是不是空的(可能在别处调用 Destroy 删除了对象,因此这里得到了空的对象引用)
- 如果对象是空的,需要创建对象,设置到 pool 中,并则设置 gameObject 为 active 并返回。
- 如果提供的 index 有可能产生数组越界,则往 pool 中添加一个新的对象,并返回。
- 当我们需要移除一个对象:
- 如果选择使用 Destroy 删除,下次获取时将得到一个空引用。处理方法在上面讲过了。
- 不使用 Destroy,而是直接置成 inactive。
- 注意将对象置成初始化状态。(如果你决定在拿对象的时候初始化也行)
- 可以写一个
PoolableMonoBehaviour 类继承 MonoBehaviour ,然后写一个 Dispose() 方法表示将该物体 “移除”(置 inactive)。
一种实现
using UnityEngine;
using System.Collections.Generic;
namespace Util {
public class GameObjectPool {
[SerializeField]
private GameObject prefab;
[SerializeField]
private List<GameObject> pool = new List<GameObject>();
private GameObjectPool() {}
public GameObjectPool(GameObject prefab) {
this.prefab = prefab;
}
private GameObject GetAndSetActiveAtIndex(int index) {
pool[index].SetActive(true);
return pool[index];
}
private GameObject GetOrCreateAtIndex(int index) {
if(pool[index] == null) {
pool[index] = CreatePrefab(true);
return pool[index];
} else {
return GetAndSetActiveAtIndex(index);
}
}
private GameObject CreatePrefab(bool setActive) {
GameObject obj = Gamemanager.InstantiateWrapper<GameObject>(prefab);
if(! setActive) {
obj.SetActive(false);
}
return obj;
}
public GameObject GetReference() {
if(pool.Count > 0) {
return GetOrCreateAtIndex(0);
} else {
return CreateAndRegister(true);
}
}
public GameObject GetReference(int index) {
if(index >= pool.Count) {
return CreateAndRegister(true);
} else {
return GetOrCreateAtIndex(index);
}
}
public GameObject CreateAndRegister(bool setActive) {
GameObject reference = CreatePrefab(setActive);
pool.Add(reference);
return reference;
}
}
}
|