Menu
CoddyTech

Unity'nin ObjectPool'ı

Coddy'nin Unity Journey'sinin Unity'de Oyun Mimarisi bölümünün bir parçası. Ders 48 / 64.

Unity, UnityEngine.Pool içindeki ObjectPool<T> havuzunu sunar. Dört lambda ile her adımda ne yapacağını belirtirsin; havuz da yedek nesneleri ve sayaçları yönetir. Bu baca her 0.2 saniyede bir duman püskürtür ve her duman püskürtüsü bir saniye sonra havuza geri döner:

using UnityEngine;
using UnityEngine.Pool;

public class Chimney : MonoBehaviour
{
    public Puff puffPrefab;
    private ObjectPool<Puff> pool;
    private float timer;

    void Awake()
    {
        pool = new ObjectPool<Puff>(
            createFunc: () => Instantiate(puffPrefab),
            actionOnGet: puff => puff.gameObject.SetActive(true),
            actionOnRelease: puff => puff.gameObject.SetActive(false),
            actionOnDestroy: puff => Destroy(puff.gameObject),
            collectionCheck: true,
            defaultCapacity: 10,
            maxSize: 20);
    }

    void Update()
    {
        timer += Time.deltaTime;
        if (timer >= 0.2f)
        {
            timer = 0f;
            Puff puff = pool.Get();
            puff.transform.position = transform.position + Vector3.up;
            puff.Launch(pool);
        }
    }
}
// Puff.cs
private IObjectPool<Puff> pool;
private float age;

public void Launch(IObjectPool<Puff> owner)
{
    pool = owner;
    age = 0f;
}

void Update()
{
    transform.Translate(0f, Time.deltaTime, 0f);
    age += Time.deltaTime;
    if (age >= 1f) pool.Release(this);
}

createFunc, Get havuzun boş olduğunu bulduğunda çalışır; actionOnGet her Get çağrısında, actionOnRelease ise her Release çağrısında çalışır. maxSize kadar yedek nesne zaten beklerken bir duman püskürtüsü serbest bırakılırsa havuz onu saklamaz: actionOnDestroy onu yok eder. defaultCapacity yalnızca havuz listesinin başlangıç boyutudur; dolayısıyla başlangıçta hiçbir nesne oluşturulmaz. CountAll, havuzun oluşturduğu ve hâlâ sahip olduğu duman püskürtülerinin sayısını; CountActive, oyunda olanların sayısını; CountInactive ise yedeklerin sayısını tutar. Get, baca onu yerleştirmeden önce duman püskürtüsünü etkinleştirir; burada sorun olmaz, çünkü Puff'ın konumunu okuyan bir OnEnable metodu yoktur. Duman püskürtüsü havuzu, her Unity havuzunun uyguladığı arayüz olan IObjectPool<Puff> olarak saklar ve Launch, her kullanımda yaşını sıfırlar.

collectionCheck açıkken, havuzda zaten bulunan bir duman püskürtüsünü serbest bırakmak bir InvalidOperationException hatasına yol açar: Trying to release an object that has already been released to the pool. Kontrol yalnızca Editor'de çalışır; çünkü her Release çağrısında yedekleri arar. Derlemede duman püskürtüsü havuzda iki kez bulunur ve iki Get çağrısı aynı nesneyi verir. Bunun yaygın bir nedeni, serbest bırakıldıktan sonra çalışmayı sürdürüp kendini tekrar serbest bırakan bir nesnedir; bu yüzden actionOnRelease nesneyi devre dışı bırakır.

challenge icon

Görev

Orta

Havai fişekleri Unity havuzundan fırlat.

  1. Launcher'ın Awake metodunda bir ObjectPool<Spark> oluştur: Instantiate(sparkPrefab) ile oluştur, havuzdan alındığında etkinleştir, bırakıldığında devre dışı bırak, havuza geri verildiğinde nesneyi yok et, maxSize: 5.
  2. Update içinde her kıvılcımı Instantiate yerine pool.Get() ile al.
  3. Return içinde Destroy yerine pool.Release(spark) çağır.

“Oyna”'ya bas ve birkaç salvo ateşle. Ardından “Kontrol et”'e bas.

Kendin dene

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Pool;

public class Launcher : MonoBehaviour
{
    public Spark sparkPrefab;
    public int sparks = 8;

    void Awake()
    {
        // Havuzu oluştur: en fazla 5 yedek kıvılcım olacak şekilde oluşturma, alma, geri bırakma ve yok etme işlemlerini tanımla
    }

    void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame)
        {
            for (int i = 0; i < sparks; i++)
            {
                Spark spark = Instantiate(sparkPrefab);
                spark.transform.position = transform.position + Vector3.up;
                spark.Launch(Direction(i), this);
            }
        }
    }

    // Ömrü bitmiş bir kıvılcım
    public void Return(Spark spark)
    {
        Destroy(spark.gameObject);
    }

    // Dışarı ve yukarı doğru, fırlatıcının çevresine eşit aralıklarla dağılmış
    Vector3 Direction(int i)
    {
        return Quaternion.Euler(0f, i * 360f / sparks, 0f) * new Vector3(0f, 1f, 3f);
    }
}

Unity'de Oyun Mimarisi bölümündeki tüm dersler

Kendi başına pratik yap: Unity playground