Unity’de Object Pooling: Instantiate’ı Neden Her Yerde Kullanmamalısın?

Merhaba arkadaşlar!

Unity’de oyun geliştirirken en sevdiğimiz kısayollardan biri şudur: ihtiyacın olunca Instantiate, işin bitince Destroy. Mermi attın, düşman spawn ettin, patlama efekti oynadın — her şey çalışıyor gibi görünür.

Ta ki oyun biraz büyüyene kadar.

Özellikle hyper-casual, match-3, shooter veya aynı anda çok obje üreten oyunlarda bu yaklaşım yavaş yavaş can sıkmaya başlar. FPS düşer, mikro takılmalar olur, Profiler’da GC Alloc satırları kırmızıya boyanır.

Bugün bu sorunun pratik bir çözümünü konuşacağız: Object Pooling.

Korkmayın — karmaşık bir mimari değil. Bir prefab’ı bir kez oluşturup tekrar tekrar kullanmak. Bu kadar. 

Instantiate / Destroy Neden Sorun Olur?

Unity’de Instantiate ve Destroy çağırdığınızda arka planda şunlar olur:

  • Yeni obje heap üzerinde oluşturulur
  • Destroy objeyi hemen silmez; işaretleme yapar
  • Biriken objeler Garbage Collection (GC) ile temizlenir
  • GC çalışırken ana thread kısa süre durabilir → frame spike

Tek bir mermi için sorun değildir. Ama şu senaryoyu düşünün:

20
mermi/sn

5
düşman spawn

1
patlama/isabet

Bu, saniyede onlarca allocation demek. Mobil cihazda oyuncu bunu “oyun takılıyor” diye hisseder.

Hatırlatma: DOTween vs PrimeTween yazısında da benzer mantığı konuşmuştuk — çok sayıda küçük maliyet birikince performans bozulur.

Object Pooling Nedir?

Object Pooling, önceden belirli sayıda obje oluşturup bunları bir havuzda tutmaktır. İhtiyaç olunca havuzdan alır, işin bitince geri verirsin.

Bu sayede runtime’da sürekli Instantiate/Destroy döngüsünden kurtulursun.

Basit Bir Pool Sınıfı

Aşağıdaki generic pool sınıfı birçok projede işinizi görür — mermi, düşman, efekt…

using System.Collections.Generic;
using UnityEngine;

public class ObjectPool<T> where T : Component
{
    private readonly T _prefab;
    private readonly Transform _parent;
    private readonly Queue<T> _pool = new();

    public ObjectPool(T prefab, int initialSize, Transform parent = null)
    {
        _prefab = prefab;
        _parent = parent;
        for (int i = 0; i < initialSize; i++)
        {
            T instance = Object.Instantiate(_prefab, _parent);
            instance.gameObject.SetActive(false);
            _pool.Enqueue(instance);
        }
    }

    public T Get(Vector3 position, Quaternion rotation)
    {
        T instance = _pool.Count > 0 ? _pool.Dequeue() : Object.Instantiate(_prefab, _parent);
        instance.transform.SetPositionAndRotation(position, rotation);
        instance.gameObject.SetActive(true);
        return instance;
    }

    public void Release(T instance)
    {
        instance.gameObject.SetActive(false);
        _pool.Enqueue(instance);
    }
}

Ne yaptık?

  • initialSize kadar obje önceden oluşturuldu
  • Get → havuzdan al
  • Release → geri ver, Destroy yok

Örnek 1: Mermi Pool’u

BulletSpawner.cs

public void Fire()
{
    Bullet bullet = _bulletPool.Get(_firePoint.position, _firePoint.rotation);
    bullet.Initialize(_bulletPool);
}

Bullet.cs — süre dolunca veya çarpınca:

_pool.Release(this);  // Destroy değil!

Örnek 2: Düşman Pool’u

Düşman öldüğünde: _pool.Release(this); — sahne sayısı sabit kalır.

Örnek 3: VFX / Patlama Efekti

Particle System bitince coroutine ile Release — her patlamada yeni prefab yok.

Ne Zaman Pool Kullanmalısın?

DurumPool?
Sık spawn/destroy (mermi, düşman, efekt)✓ Evet
Mobil / hyper-casual / yoğun gameplay✓ Evet
Profiler’da GC spike görüyorsan✓ Evet
Saniyede 5+ kez aynı prefab✓ Evet
Ana menüde bir kez açılan popup✗ Hayır
Sahne geçişinde bir kez yüklenen obje✗ Hayır
Toplam 3–5 adet statik obje✗ Hayır

Kural: Kısa ömürlü + sık tekrar → pool. Nadir + uzun süre sahnede → normal Instantiate.

Dikkat Etmen Gereken 4 Şey

  1. Obje durumunu sıfırlaOnEnable içinde health, velocity vb.
  2. Havuz boyutu — Profiler peak kullanımına göre initialSize ayarla.
  3. Parent Transform — BulletPool, EnemyPool altında topla.
  4. UnityEngine.Pool — Unity 2021+ hazır API; mantık aynı.

Pool vs Instantiate

 Instantiate / DestroyObject Pool
GC baskısıYüksekDüşük
Spawn hızıHer seferinde allocationHavuzdan anında
BellekDalgalanmaÖnceden ayrılmış
Mobil oyunlarRiskliÖnerilir

Sonuç

Object Pooling, doğru yerde kullanıldığında standart bir alışkanlık.

  • Sık spawn → Instantiate/Destroy döngüsünden kaçın
  • Profiler’da GC Alloc varsa pool adayı var demektir
  • Kısa ömürlü + sık tekrar = pool adayı

Bir kez dene — Profiler farkını görünce geri dönmek istemezsin. 

Bir Yanıt Bırakın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir