Unity对象池应用
程序员文章站
2022-07-13 15:00:51
...
对象池,顾名思义,就是放对象的池子。
优缺点:空间换时间,用额外的内存消耗减少创建物体的时间,保证运行的流畅度。
何时用:当需要大量创建相同游戏物体时,例如射击游戏中不停的创建子弹销毁子弹。大量地创建物体销毁物体会造成游戏卡顿,为了解决这个方法可以采用对象池。
实现
1、需要一个容器,用来实现存放对象
2、一个取对象的方法
3、一个存对象的方法
详细代码如下:
关键地方已写了详细备注。
public class GameObjectPool : MonoBehaviour {
private static GameObjectPool instance;
public static GameObjectPool Instance
{
get
{
if (instance == null)
{
GameObject obj = new GameObject("GameObjectPool");
instance = obj.AddComponent<GameObjectPool>();
DontDestroyOnLoad(obj);
}
return instance;
}
}
//对象池
public Dictionary<string, List<GameObject>> cache;
public GameObject GetGameObject(string key, GameObject prefab, Vector3 position)
{
GameObject obj;
//如果字典中存在当前键,并且当前键的对象池里有未使用的游戏物体,则直接拿出一个使用。
if (cache.ContainsKey(key) && cache[key].Where(w => w.activeSelf == false).Count() > 0)
{
obj = cache[key][0];
cache[key].RemoveAt(0);
}
//如果字典中存在当前键,但是当前键的对象池物体已经被用完,则根据传进来的预制体新创建一个。
else if (cache.ContainsKey(key) && cache[key].Where(w => w.activeSelf == false).Count() <= 0)
{
obj = Instantiate(prefab) as GameObject;
}
//字典中不存在当前键,新建一个键与相对应的对象池,并根据传进来的预制体新创建一个
else
{
obj = Instantiate(prefab) as GameObject;
cache.Add(key, new List<GameObject>());
}
obj.SetActive(true);
obj.transform.position = position;
obj.transform.SetParent(transform);
return obj;
}
//延迟回收
public void CollectObject(string key,GameObject obj)
{
StartCoroutine(DelayCollect(key, obj));
}
//回收对象池的方法,将物体放回对象池并且设置为false
IEnumerator DelayCollect(string key,GameObject obj)
{
cache[key].Add(obj);
yield return new WaitForSeconds(2);
obj.SetActive(false);
}
//清空对象池中没有使用的对象
public void Clear(string key)
{
if (cache.ContainsKey(key))
{
//Destroy当中所有的对象
for (int i = 0; i < cache[key].Where(w => w.activeSelf == false).Count(); i++)
{
Destroy(cache[key][i]);
}
if (cache[key].Where(w => w.activeSelf == true).Count() <= 0)
cache.Remove(key);
}
}
public void AllClear()
{
var list = new List<string>(cache.Keys);
for (int i = 0; i < list.Count; i++)
{
Clear(list[i]);
}
}
private void Awake()
{
cache = new Dictionary<string, List<GameObject>>();
}
}
对象池测试脚本代码如下:
public class PoolTest : MonoBehaviour {
GameObject prefabCube;
GameObject prefabSphere;
GameObject go;
private void Start()
{
Init();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
go = GameObjectPool.Instance.GetGameObject("Cube", prefabCube, new Vector3(Random.Range(-4, 4), Random.Range(-4, 4), 0));
go.name = "Cube";
GameObjectPool.Instance.CollectObject(go.name, go);
}
if (Input.GetKeyDown(KeyCode.S))
{
go = GameObjectPool.Instance.GetGameObject("Sphere", prefabSphere, new Vector3(Random.Range(-4, 4), Random.Range(-4, 4), 0));
go.name = "Sphere";
GameObjectPool.Instance.CollectObject(go.name, go);
}
if (Input.GetKeyDown(KeyCode.D))
{
GameObjectPool.Instance.AllClear();
}
}
private void Init()
{
prefabCube = Resources.Load("Prefab/Cube") as GameObject;
prefabSphere = Resources.Load("Prefab/Sphere") as GameObject;
}
}
实际演示效果:
可以看到,当对象池有物体时并且可用数量不为0时,会直接将对象池的物体拿来用,不存在或者可用数量为0时才会新建。
ps:实现对象池的方式多种多样,什么时候回收也要根据具体的条件做适应的修改,这里只是做一个最简单的案例。
案例Demo