| Engine | Unity 2022.3 LTS or newer |
|---|---|
| Language | C# |
| Attach to | A manager GameObject |
| Time to run | ~3 minutes |
| Level | Intermediate |
| Output | One file + small IPooled interface (~90 lines) |
| Dependencies | None. |
The prompt
Open a fresh chat at claude.ai/new, paste, and send.
$ Build me a reusable Unity 2022.3+ object pool as a single C# file. // shape - One file, ObjectPool.cs, a MonoBehaviour on a manager GameObject. - [SerializeField] GameObject prefab. - [SerializeField] int initialSize, default 20. - [SerializeField] bool canExpand, default true. // interface - Define a small interface IPooled with OnSpawned() and OnDespawned(). - Pooled objects may implement it to reset their own state; the pool calls the hooks if present (GetComponent, null-safe). // behaviour - Prewarm initialSize inactive instances in Awake, parented under this transform. - Get(Vector3 position, Quaternion rotation): take an inactive instance (or expand by one if none free and canExpand), position it, SetActive(true), call OnSpawned, return it. - Return(GameObject obj): SetActive(false), call OnDespawned. Guard against returning something that is not from this pool. - Use a Queue or a List of the instances. No LINQ in the hot path. // constraints - No Instantiate/Destroy after prewarm unless expanding. - No magic numbers. Everything tunable is SerializeField. - Null-safe: Get returns null and logs a warning if empty and canExpand is false. // return format - One file. No prose before or after the code. - Brief comments only on the expand path and the IPooled hooks.
What this gets you
A pool you can point at any prefab, bullets, enemies, coins, hit sparks, and reuse instead of churning. The frame-time graph goes flat where it used to spike, and your bullet hell stops hitching every time the GC wakes up.
The IPooled hook means reused objects reset cleanly, so you never get the classic pooling bug where a recycled enemy spawns already half-dead or a bullet keeps last frame's velocity.
Why it matters
GC spikes are invisible until they are not
Instantiate and Destroy feel free at 60fps in an empty scene. Then you ship a boss that fires 200 bullets and the game stutters on slower machines. Pooling removes the allocation entirely, so performance stays flat regardless of spawn rate.
Reset on spawn, not on death
The subtle pooling bug: an object carries its old state into its next life. Resetting in OnSpawned (when it leaves the pool) rather than trusting the last user to clean up is what keeps recycled objects correct.
One pool, many uses
A prefab-agnostic pool means you write this once and reuse it for every spawner in the game. That is the whole point of asking for a generic component instead of a bullet-specific one.
What good output looks like
- No Instantiate in Get after prewarm. Get should pull from the free list. Instantiate only appears in the prewarm loop and the optional expand branch. If Get instantiates every call, it is not a pool.
- OnSpawned/OnDespawned are called via a null-safe check. Look for
TryGetComponent<IPooled>or a cached reference, not a hard cast that throws when the object does not implement it. - Objects are deactivated on Return, not destroyed.
SetActive(false)on return,SetActive(true)on get. No Destroy. - Return validates ownership. Returning a foreign object should be ignored or warned, not blindly added to the free list, or the pool starts handing out objects it does not own.
How to wire it up
- Save the output as
ObjectPool.cs. - Create an empty GameObject (for example
BulletPool) and attach the script. - Assign your bullet prefab and set the initial size to a bit more than the most you expect on screen.
- In your firing code, replace
Instantiate(bullet, pos, rot)withpool.Get(pos, rot). - When the bullet dies (off screen, on hit), call
pool.Return(gameObject)instead ofDestroy.
Common gotchas
Recycled objects keep old state
You are not resetting on spawn. Implement IPooled.OnSpawned on the pooled object and reset health, velocity and timers there.
Bullets stop appearing after a while
The pool ran dry with canExpand off, so Get returned null. Either raise initialSize or turn canExpand on. Check for the null-return warning in the console.
Returned objects still collide or render
Return is not actually deactivating them. Confirm SetActive(false) runs in Return and that nothing re-enables them on the same frame.
Where to go next
- Multiple pools. A dictionary of pools keyed by prefab so one manager serves bullets, enemies and effects.
- Auto-return. A small component that returns an object to its pool after N seconds, perfect for hit sparks.
- Feed a spawner. Pair this with an enemy patrol or wave system so spawns cost nothing.
Use it however you want
The prompt is CC0, paste it, modify it, ship a game with it. The output Claude gives you is yours. A link back to pixeldex.dev in your project's README helps the next solo dev find this stuff.