使用队列实现对象池
本课属于 Coddy Unity 学习旅程的“Unity 游戏架构”单元。第 47 课,共 64 课。
对象池需要一个地方来存放备用对象,C# 为此提供了集合 Queue<T>:Enqueue 会在队尾添加一个项目,Dequeue 会取出队首的项目,Count 会显示有多少个项目在等待。这个水族箱每 0.3 秒会冒出一个气泡,每个气泡到达水面后都会回到对象池:
using System.Collections.Generic;
using UnityEngine;
public class BubblePool : MonoBehaviour
{
public GameObject bubblePrefab;
private Queue<GameObject> pool = new Queue<GameObject>();
private float timer;
void Update()
{
timer += Time.deltaTime;
if (timer >= 0.3f)
{
timer = 0f;
Get(new Vector3(Random.Range(-2f, 2f), 0f, 0f));
}
}
public GameObject Get(Vector3 position)
{
GameObject bubble;
if (pool.Count > 0)
{
bubble = pool.Dequeue();
}
else
{
bubble = Instantiate(bubblePrefab);
bubble.GetComponent<Bubble>().pool = this;
}
bubble.transform.position = position;
bubble.SetActive(true);
return bubble;
}
public void Return(GameObject bubble)
{
bubble.SetActive(false);
pool.Enqueue(bubble);
}
}// Bubble.cs,挂在气泡预制体上
public BubblePool pool;
void Update()
{
transform.Translate(0f, 2f * Time.deltaTime, 0f);
if (transform.position.y > 5f) pool.Return(gameObject);
}Get 会先检查 Count,因为对空队列调用 Dequeue 会抛出 InvalidOperationException。只有在这之后才会调用 Instantiate,而且只有新气泡需要设置它的 pool 字段:复用的气泡仍保留该字段。队列会先交出等待时间最长的气泡,也就是先进先出。气泡到达水面时会调用 Return,而不是 Destroy;它会留在场景中并处于非激活状态,直到下一次调用 Get。
Get 会先设置气泡的位置,再调用 SetActive(true),因为启用气泡会立即运行它的 OnEnable,而其中的任何操作都应该看到新位置,而不是它上次消失时的位置。复用对象也会在这里重新开始:Awake 和 Start 在创建气泡时只运行一次,而 OnEnable 会在每次调用 Get 时运行。
挑战
中等将脚印放入对象池,而不是销毁它们。
- 在
Trail中,用Queue<GameObject>保存备用对象。Return(footprint)会将脚印禁用并将其加入队列;Start会创建prewarm个脚印并将它们放回队列。 Get(position)会从队列中取出一个备用脚印(如果没有,就创建一个),设置它的位置并将它启用。- 在
Footprint中,替换Destroy:在OnEnable中重置年龄;经过life秒后,调用trail.Return(gameObject)。
按“开始”并行走:脚印会在 1.5 秒后消失。然后按“检查”。
亲手试试
using System.Collections.Generic;
using UnityEngine;
public class Trail : MonoBehaviour
{
public GameObject footprintPrefab;
public float step = 1f;
public int prewarm = 6;
private Vector3 lastDrop;
void Start()
{
lastDrop = transform.position;
// 用 prewarm 个脚印填充对象池
}
void Update()
{
if (Vector3.Distance(transform.position, lastDrop) >= step)
{
lastDrop = transform.position;
Get(new Vector3(transform.position.x, 0.02f, transform.position.z));
}
}
public GameObject Get(Vector3 position)
{
GameObject footprint = Instantiate(footprintPrefab, position, Quaternion.identity);
footprint.GetComponent<Footprint>().trail = this;
return footprint;
}
public void Return(GameObject footprint)
{
// 备用脚印在对象池中等待使用
}
}
Unity 游戏架构中的所有课
自己练习: Unity 在线编辑器