Object Pooling In Unity

Last Updated : 4 May, 2026

Object pooling is a performance optimization technique. Instead of destroying an object, you deactivate it and add it back to a pool. When needed again, you reuse it instead of creating a new one.

  • No repeated Instantiate() and Destroy() calls
  • Reduces garbage collection and lag spikes
  • Essential for mobile games and bullet-hell shooters

Problem without Pooling

When you instantiate and destroy objects frequently, Unity keeps allocating and freeing memory. This causes lag spikes.

C#
// Bad for performance - creates garbage
void Shoot()
{
    GameObject bullet = Instantiate(bulletPrefab);
    Destroy(bullet, 2f);  // Creates garbage collection
}

Every time you destroy an object, Unity's garbage collector has to clean it up later, causing frame drops.

With Pooling

Create a pool of objects at start. Reuse them instead of destroying.

C#
public class BulletPool : MonoBehaviour
{
    public GameObject bulletPrefab;
    public int poolSize = 20;
    private Queue<GameObject> pool = new Queue<GameObject>();
    void Start()
    {
        // Create all bullets at start
        for (int i = 0; i < poolSize; i++)
        {
            GameObject bullet = Instantiate(bulletPrefab);
            bullet.SetActive(false);  // Deactivate
            pool.Enqueue(bullet);      // Add to queue
        }
    }
    public GameObject GetBullet()
    {
        if (pool.Count > 0)
        {
            GameObject bullet = pool.Dequeue();
            bullet.SetActive(true);
            return bullet;
        }
        else
        {
            // Pool empty - create new (optional)
            return Instantiate(bulletPrefab);
        }
    }
    public void ReturnBullet(GameObject bullet)
    {
        bullet.SetActive(false);
        pool.Enqueue(bullet);
    }
}

Using the Object Pool

Here's how to shoot using the object pool.

C#
public class Shooter : MonoBehaviour
{
    public BulletPool bulletPool;
    public Transform firePoint;
    
    void Update()
    {
        if (Input.GetButtonDown("Fire1"))
        {
            Shoot();
        }
    }
    void Shoot()
    {
        // Get bullet from pool
        GameObject bullet = bulletPool.GetBullet();
        bullet.transform.position = firePoint.position;
        bullet.transform.rotation = firePoint.rotation;
        
        // Set velocity
        Rigidbody rb = bullet.GetComponent<Rigidbody>();
        rb.velocity = firePoint.forward * 20f;
        
        // Return to pool after 2 seconds
        StartCoroutine(ReturnAfterDelay(bullet, 2f));
    }
    IEnumerator ReturnAfterDelay(GameObject bullet, float delay)
    {
        yield return new WaitForSeconds(delay);
        bulletPool.ReturnBullet(bullet);
    }
}

Generic Object Pool (Reusable)

Create one pool that works for any type of object.

C#
public class ObjectPool<T> where T : Component
{
    private T prefab;
    private Queue<T> pool = new Queue<T>();
    public ObjectPool(T prefab, int initialSize)
    {
        this.prefab = prefab;
        
        for (int i = 0; i < initialSize; i++)
        {
            T obj = GameObject.Instantiate(prefab);
            obj.gameObject.SetActive(false);
            pool.Enqueue(obj);
        }
    }
    public T Get()
    {
        if (pool.Count > 0)
        {
            T obj = pool.Dequeue();
            obj.gameObject.SetActive(true);
            return obj;
        }
        else
        {
            return GameObject.Instantiate(prefab);
        }
    }
    public void Return(T obj)
    {
        obj.gameObject.SetActive(false);
        pool.Enqueue(obj);
    }
}

Usage:

C#
public class GameManager : MonoBehaviour
{
    public GameObject bulletPrefab;
    private ObjectPool<Bullet> bulletPool;
    void Start()
    {
        bulletPool = new ObjectPool<Bullet>(bulletPrefab.GetComponent<Bullet>(), 30);
    }
}

When to Use Object Pooling

Good for

  • Bullets in shooting games.
  • Enemies that spawn frequently.
  • Particle effects (explosions, dust).
  • Pickups (coins, health items).

Not needed for

  • UI elements.
  • Bosses (spawn rarely).
  • Scene objects.
  • Static environment.

Without Pooling vs With Pooling

FeatureWithout PoolingWith Pooling
Object HandlingUses Instantiate() and Destroy() repeatedlyReuses objects (activate/deactivate)
Performance ImpactCauses garbage collection → lag spikesAvoids garbage collection overhead
SpeedSlowerMuch faster
ImplementationSimple to implementRequires extra setup/code
Comment
Article Tags:

Explore