Menu
CoddyTech

ObjectPool di Unity

Fa parte della sezione Architettura dei giochi in Unity del percorso Unity di Coddy. Lezione 48 di 64.

Unity include un pool, ObjectPool<T> in UnityEngine.Pool. Gli dici cosa fare a ogni passaggio con quattro lambda, e il pool conserva gli elementi di riserva e i conteggi. Questo camino emette una nuvola di fumo ogni 0.2 secondi e ogni nuvola torna dopo un secondo:

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 viene eseguita quando Get trova il pool vuoto; actionOnGet viene eseguita a ogni Get e actionOnRelease a ogni Release. Quando una nuvola viene rilasciata mentre nel pool sono già in attesa maxSize elementi di riserva, il pool non la conserva: actionOnDestroy la distrugge. defaultCapacity è solo la capacità iniziale dell'elenco del pool, quindi non viene creato nulla in anticipo. CountAll conta le nuvole create dal pool e ancora possedute, CountActive quelle presenti nel gioco e CountInactive quelle di riserva. Get ha attivato la nuvola prima che il camino la posizioni, il che va bene in questo caso perché Puff non ha un OnEnable che legga la sua posizione. La nuvola conserva il pool come IObjectPool<Puff>, l'interfaccia implementata da ogni pool Unity, e Launch azzera la sua età a ogni utilizzo.

Con collectionCheck attivo, rilasciare una nuvola che si trova già nel pool genera un'eccezione InvalidOperationException: Trying to release an object that has already been released to the pool. Il controllo viene eseguito solo nell'Editor, perché a ogni Release cerca tra gli elementi di riserva; in una build la nuvola resterebbe due volte nel pool e due Get restituirebbero lo stesso oggetto. Una causa comune è un oggetto che continua a funzionare dopo il rilascio e si rilascia di nuovo, motivo per cui actionOnRelease lo disattiva.

challenge icon

Sfida

Medio

Fai esplodere i fuochi d’artificio usando il pool di Unity.

  1. In Awake di Launcher, crea un ObjectPool<Spark>: crea gli oggetti con Instantiate(sparkPrefab), attivali quando vengono prelevati, disattivali quando vengono rilasciati, distruggi l’oggetto quando viene restituito e imposta maxSize: 5.
  2. In Update, preleva ogni scintilla con pool.Get() invece di Instantiate.
  3. In Return, chiama pool.Release(spark) invece di Destroy.

Premi «Gioca» e lancia alcune raffiche. Poi premi «Verifica».

Provalo tu

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

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

    void Awake()
    {
        // Crea il pool: crea, ottieni, rilascia e distruggi, con al massimo 5 scintille di riserva
    }

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

    // Una scintilla che ha esaurito il suo ciclo di vita
    public void Return(Spark spark)
    {
        Destroy(spark.gameObject);
    }

    // Verso l'esterno e verso l'alto, distribuite uniformemente intorno al lanciatore
    Vector3 Direction(int i)
    {
        return Quaternion.Euler(0f, i * 360f / sparks, 0f) * new Vector3(0f, 1f, 3f);
    }
}

Tutte le lezioni di Architettura dei giochi in Unity

Esercitati da solo: Playground Unity