Menu
CoddyTech

ObjectPool w Unity

Część sekcji Architektura gier w Unity ścieżki Unity w Coddy. Lekcja 48 z 64.

Unity udostępnia pulę ObjectPool<T> w UnityEngine.Pool. Określasz jej działanie na każdym etapie za pomocą czterech lambd, a ona przechowuje zapasowe obiekty i liczniki. Ten komin wypuszcza kłęby dymu co 0.2 sekundy, a każdy kłąb wraca po sekundzie:

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 jest wywoływana, gdy Get nie znajduje obiektów w puli; actionOnGet jest wywoływana przy każdym Get, a actionOnRelease przy każdym Release. Gdy kłąb zostanie zwolniony, a w puli czeka już maxSize obiektów zapasowych, pula go nie zachowuje: actionOnDestroy go niszczy. defaultCapacity określa tylko początkowy rozmiar listy puli, więc żadne obiekty nie są tworzone z góry. CountAll zlicza kłęby utworzone przez pulę i nadal przez nią posiadane, CountActive — te znajdujące się w grze, a CountInactive — zapasowe. Get włącza kłąb, zanim komin go umieści, co tutaj nie stanowi problemu, ponieważ Puff nie ma OnEnable, który odczytuje jego pozycję. Kłąb przechowuje pulę jako IObjectPool<Puff> — interfejs implementowany przez każdą pulę Unity — a Launch resetuje jego wiek przy każdym użyciu.

Gdy collectionCheck jest włączone, zwolnienie kłębu, który już znajduje się w puli, powoduje zgłoszenie wyjątku InvalidOperationException: Trying to release an object that has already been released to the pool. Sprawdzanie odbywa się tylko w Edytorze, ponieważ przy każdym Release przeszukuje obiekty zapasowe; w kompilacji kłąb znalazłby się w puli dwukrotnie, a dwa wywołania Get zwróciłyby ten sam obiekt. Częstą przyczyną jest obiekt, który nadal działa po zwolnieniu i zwalnia się ponownie, dlatego actionOnRelease go wyłącza.

challenge icon

Wyzwanie

Średni

Odpal fajerwerki z puli Unity.

  1. W Awake klasy Launcher utwórz ObjectPool<Spark>: twórz obiekty za pomocą Instantiate(sparkPrefab), włączaj je po pobraniu, wyłączaj po zwróceniu, niszcz obiekt po zwolnieniu i ustaw maxSize: 5.
  2. W Update pobieraj każdą iskrę za pomocą pool.Get() zamiast Instantiate.
  3. W Return wywołaj pool.Release(spark) zamiast Destroy.

Naciśnij „Graj” i odpal kilka serii fajerwerków. Następnie naciśnij „Sprawdź”.

Spróbuj swoich sił

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

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

    void Awake()
    {
        // Utwórz pulę: twórz, pobieraj, zwalniaj i niszcz obiekty, przechowując maksymalnie 5 zapasowych iskier
    }

    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);
            }
        }
    }

    // Iskra, której czas życia dobiegł końca
    public void Return(Spark spark)
    {
        Destroy(spark.gameObject);
    }

    // Na zewnątrz i w górę, równomiernie wokół wyrzutni
    Vector3 Direction(int i)
    {
        return Quaternion.Euler(0f, i * 360f / sparks, 0f) * new Vector3(0f, 1f, 3f);
    }
}

Wszystkie lekcje w sekcji Architektura gier w Unity

Poćwicz samodzielnie: Edytor online Unity