Menu
CoddyTech

Unity'nin ObjectPool'ı

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

Unity, UnityEngine.Pool içindeki ObjectPool<T> havuzunu sunar. Dört lambda ile her adımda ne yapacağını belirtirsin; havuz da yedekleri ve sayımları yönetir. Bu baca her 0.2 saniyede bir duman çıkarır ve her duman püskürtüsü bir saniye sonra havuza 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 havuzu boş bulduğunda çalışır; actionOnGet her Get işleminde, actionOnRelease ise her Release işleminde çalışır. Bir duman püskürtüsü, maxSize kadar yedek zaten beklerken havuza bırakılırsa havuz onu saklamaz: actionOnDestroy onu yok eder. defaultCapacity, havuz listesinin yalnızca başlangıç boyutudur; bu yüzden önceden hiçbir şey oluşturulmaz. CountAll, havuzun oluşturduğu ve hâlâ sahip olduğu duman püskürtülerini; CountActive, oyunda olanları; CountInactive ise yedekleri sayar. Get, baca onu yerleştirmeden önce duman püskürtüsünü etkinleştirmiştir; burada bu sorun olmaz, çünkü Puff'ın konumunu okuyan bir OnEnable'ı yoktur. Duman püskürtüsü, her Unity havuzunun uyguladığı arayüz olan IObjectPool<Puff> türünde havuza başvuruyu tutar ve Launch her kullanımda yaşını sıfırlar.

collectionCheck açıkken, zaten havuzda olan bir duman püskürtüsünü havuza bırakmak InvalidOperationException hatası verir: Trying to release an object that has already been released to the pool. Yedekleri her Release işleminde aradığı için bu kontrol yalnızca Editor'da çalışır; derlemede duman püskürtüsü havuzda iki kez bulunur ve iki Get çağrısı aynı nesneyi döndürür. Bunun yaygın bir nedeni, havuza bırakıldıktan sonra çalışmaya devam edip kendini yeniden havuza bırakan bir nesnedir; actionOnRelease'ın onu devre dışı bırakmasının nedeni de budur.

challenge icon

Görev

Orta

Havai fişekler. Boşluk tuşuna her basışta fırlatıcıdan 8 kıvılcım fırlar ve her Spark, 1 saniyelik ömrü sona erdiğinde fırlatıcının Return(this) metodunu çağırır (Spark hazır). Bugün fırlatıcı her kıvılcımı Instantiate eder ve Destroy eder. Unity'nin havuzunu kullan:

  • Launcher.cs içinde Awake metodunda bir ObjectPool<Spark> oluştur. Bu havuz, sparkPrefab örneğini oluşturarak bir kıvılcım yaratır, kıvılcım havuzdan alındığında onu etkinleştirir, geri geldiğinde devre dışı bırakır ve havuza bırakıldığında kıvılcımın nesnesini yok eder.
  • Havuz en fazla 5 yedek kıvılcım tutar; bu nedenle 8 kıvılcımlık bir patlamadan sonra 5'i bekler, 3'ü yok edilir.
  • Boşluk tuşu, 8 kıvılcımını örnek oluşturmak yerine havuzdan alır ve Return, kıvılcımı yok etmek yerine havuza geri bırakır.

Oynat'a bas, birkaç patlama gerçekleştir, 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, 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ü sona ermiş 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 yayılmış
    Vector3 Direction(int i)
    {
        return Quaternion.Euler(0f, i * 360f / sparks, 0f) * new Vector3(0f, 1f, 3f);
    }
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

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