Skip to content

Tutorial: Object Pooling with PooledObjectCreator

Spawn and reclaim GameObjects without the per-frame allocation cost of Instantiate / Destroy. PooledObjectCreator hands out reused instances from a pool; consumers ask for an item via Create, hand it back via Reclaim. Bullets, particle bursts, UI cards, enemy waves — anything that churns.

By the end of this tutorial you'll have a pool that pre-spawns 20 bullet instances and dispenses / reclaims them without garbage spikes.

Prerequisites: Familiarity with Unity prefabs.


How It Works

flowchart LR
    P["Pool<br/>(inactive reserve)"]
    A["Active items"]
    Consumer["Consumer<br/>(bullet spawner)"]
    Consumer -->|"Create()"| P
    P -->|"obj"| Consumer
    Consumer -->|"Reclaim(obj)"| A
    A -->|"released"| P

A PooledObjectCreator:

  • Pre-instantiates up to initialCount on Awake.
  • Grows on demand up to maxCount (0 for unlimited).
  • Routes returned instances back into the inactive reserve.
  • Caps the live "in-use" set via maxActive (force-reclaims the oldest when exceeded).

Every instance carries a PoolObject component (added automatically if the prefab doesn't have one) — that's what links it back to the creator and lets it participate in the acquire / release dance.


1. Prepare the Prefab

Make your prefab (e.g. Bullet). Add a PoolObject component to the root.

The PoolObject component is what tracks "which pool I belong to" and exposes the Acquire / Release hooks. If you forget to add it, the creator will add one at runtime and warn — fine for prototyping, but author it explicitly for production.


2. Add the Pooled Object Creator

In the scene, on a long-lived GameObject (e.g. BulletPool), add a PooledObjectCreator.

Add Component → Jungle → Creator → Pooled Object Creator

Configure:

Field Value
prefab Drag your Bullet prefab in.
initialCount 20 (pre-spawn 20 on Awake).
maxCount 100 (cap total pool size; 0 for unlimited).
poolParent Optional parent transform for inactive instances. Defaults to the creator's transform.
maxActive 0 (no cap on active items). Set to N to force-reclaim the oldest when more than N are in use.

Press Play. Open the hierarchy — under the creator you'll see 20 inactive Bullet instances waiting.


3. Acquire and Release from Code

The simplest API:

[SerializeField] private PooledObjectCreator bulletPool;

public void Fire() {
    bulletPool.Create(bullet => {
        // bullet is the GameObject, freshly activated.
        // Apply velocity, sound, etc.
    });
}

public void Recycle(GameObject bullet) {
    bulletPool.Reclaim(bullet, () => {
        // Optional: notify other systems the bullet is back.
    });
}

Create is async-style — it gives you a callback when the instance is ready (most pools complete it synchronously, but the API leaves room for grace periods or fade-in animations on Acquire).

Reclaim does the inverse — disables the GameObject, resets pool state, returns it to the inactive reserve. The optional callback fires when reclaim is complete.


4. Acquire and Release Without Writing Code

Octoputs and Jungle ship operations that drive creators directly. Look for CreateOperation, ReclaimOperation, DestroyOperation in any operation list picker. Wire them to:

  • Reactor body — spawn a bullet whenever a "fire" signal arrives.
  • AttachingAgent — let the agent reclaim Attachables it can't place anywhere.
  • ProcessComponent — fire-and-forget spawn from a process.

The point of using the creator as the indirection is that the who (spawner) and how (instantiate vs pool) are independent. Swap PooledObjectCreator for InstanceObjectCreator and the same spawn calls work — just without reuse.


5. Runtime State Probes

Probe Returns
InactiveCount Items currently in the reserve (ready to acquire).
ActiveCount Items currently in use.
TotalCount Active + Inactive.
Inactive An IItemSource<GameObject> view of the reserve.

Useful for HUD displays ("ammo: 12/20"), debug overlays, and writing conditions that gate behaviour on pool state.


6. Bulk Operations

Method What it does
ReleaseAll() Force-reclaim every active instance back to the pool. Useful on scene reset.
DestroyItem(obj) Permanently remove an instance (e.g. if it was corrupted).

7. PooledObjectCreator vs InstanceObjectCreator

Creator Behaviour
PooledObjectCreator Reuse instances. Create = pull from pool (or grow). Reclaim = return to pool.
InstanceObjectCreator One-off. Create = Instantiate. Reclaim = Destroy.

Both implement the same interface (IItemCreator<GameObject>) — consumers don't care which is wired underneath. Use Instance for "one-shot, throwaway" spawns (debug helpers, short-lived effects you don't care about pooling). Use Pooled when the same kind of object is acquired and released repeatedly.


8. Container Wrappers

Sometimes you want to talk to a container of pooled objects rather than the pool itself ("give me one", "store this one back", "how many are available"). Drop a GameObjectCreatorBackedContainer and point it at the PooledObjectCreator. Now you have:

Container op Delegates to
TryTake / Take creator.Create
TryReturn / Store creator.Reclaim
TryRemove creator.DestroyItem

Containers fit anywhere an IItemContainer<GameObject> is expected — agents, transfer commands, UI bindings. The pool stays in charge of the lifecycle, the container exposes it in take/store vocabulary.


Next Steps