A Pool with a Queue
Part of the Game Architecture in Unity section of Coddy's Unity journey. Lesson 38 of 50.
A pool needs somewhere to keep its spare objects, and C# has the collection for it, Queue<T>: Enqueue adds an item at the back, Dequeue takes the one at the front, and Count says how many wait. This aquarium sends up a bubble every 0.3 seconds, and each bubble comes back to the pool at the surface:
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, on the bubble prefab
public BubblePool pool;
void Update()
{
transform.Translate(0f, 2f * Time.deltaTime, 0f);
if (transform.position.y > 5f) pool.Return(gameObject);
}Get checks Count first, because Dequeue on an empty queue throws an InvalidOperationException. Only then does it Instantiate, and only a new bubble needs its pool field set: a reused one still has it. The queue hands out the bubble that has waited longest, first in, first out. A bubble at the surface calls Return instead of Destroy, and stays in the scene, inactive, until the next Get.
Get places the bubble before SetActive(true), because switching it on runs its OnEnable straight away, and anything there should see the new position, not where it last vanished. That is also where a reused object starts over: Awake and Start ran once, when the bubble was made, and OnEnable runs on every Get.
Challenge
MediumFootprints in the sand. The arrows walk the Player (ready). Trail.cs, on the Player, drops a footprint every 1 unit walked by calling Get(position), and each Footprint lasts life (1.5) seconds. Today Get instantiates a new footprint every time and each one destroys itself. Pool them:
- In
Trail.cs, keep the spare footprints in aQueue<GameObject>. Every footprint the Trail makes needs itstrailfield set to this Trail. - In Start, fill the pool with
prewarm(6) footprints, made and switched off, so the first steps make nothing new. Get(position)takes a footprint from the queue when it has one, and makes a new one only when it is empty; then it places the footprint and switches it on.Return(footprint)switches the footprint off and puts it in the queue.- In
Footprint.cs, a footprint counts its age and, afterlifeseconds, goes back withtrail.Return(gameObject)instead of being destroyed. A reused footprint lasts its full 1.5 seconds again.
Press Play, walk around for a while, then Check.
Try it yourself
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;
// Fill the pool with prewarm footprints
}
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)
{
// Spare footprints wait in the pool
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Game Architecture in Unity
1Components, Not Hierarchies
Small ComponentsA Base Enemy ScriptAbstract ComponentsComponents Working TogetherRecap: Modular Turret10Project: Arena Survivor
The HeroEnemy Types