Menu
CoddyTech

Unitys ObjectPool

Teil des Abschnitts Spielarchitektur in Unity der Unity-Journey von Coddy. Lektion 48 von 64.

Unity enthält einen Pool: ObjectPool<T> in UnityEngine.Pool. Mit vier Lambdas legst du fest, was in jedem Schritt geschehen soll, und der Pool verwaltet die Ersatzobjekte und die Anzahlen. Dieser Schornstein stößt alle 0.2 Sekunden Rauch aus, und jede Rauchwolke wird nach einer Sekunde zurückgegeben:

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 wird ausgeführt, wenn Get den Pool leer vorfindet; actionOnGet wird bei jedem Get und actionOnRelease bei jedem Release ausgeführt. Wird eine Rauchwolke freigegeben, während bereits maxSize Ersatzobjekte warten, behält der Pool sie nicht: actionOnDestroy zerstört sie. defaultCapacity ist nur die Anfangsgröße der Liste des Pools; es werden also keine Objekte vorab erstellt. CountAll zählt die Rauchwolken, die der Pool erstellt hat und noch besitzt, CountActive diejenigen, die im Spiel aktiv sind, und CountInactive die Ersatzobjekte. Get aktiviert die Rauchwolke, bevor der Schornstein sie platziert. Das ist hier in Ordnung, da Puff kein OnEnable hat, das ihre Position ausliest. Die Rauchwolke speichert den Pool als IObjectPool<Puff>, der Schnittstelle, die alle Unity-Pools implementieren, und Launch setzt ihr Alter bei jeder Verwendung zurück.

Ist collectionCheck aktiviert, löst die Freigabe einer Rauchwolke, die bereits im Pool ist, eine InvalidOperationException aus: Trying to release an object that has already been released to the pool. Die Prüfung wird nur im Editor ausgeführt, weil sie bei jedem Release die Ersatzobjekte durchsucht; in einem Build läge die Rauchwolke zweimal im Pool, und zwei Gets würden dasselbe Objekt ausgeben. Eine häufige Ursache ist ein Objekt, das nach seiner Freigabe weiterläuft und sich erneut freigibt. Deshalb schaltet actionOnRelease es aus.

challenge icon

Aufgabe

Mittel

Lass das Feuerwerk aus Unitys Pool explodieren.

  1. Erstelle in Awake von Launcher einen ObjectPool<Spark>: Erzeuge Objekte mit Instantiate(sparkPrefab), aktiviere sie beim Abrufen, deaktiviere sie beim Freigeben und zerstöre das Objekt beim Freigeben; setze maxSize: 5.
  2. Rufe in Update jeden Funken mit pool.Get() ab, statt Instantiate zu verwenden.
  3. Rufe in Return pool.Release(spark) auf, statt Destroy zu verwenden.

Drücke auf „Spielen“ und feuere ein paar Salven ab. Klicke dann auf „Prüfen“.

Probier es selbst

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

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

    void Awake()
    {
        // Erstelle den Pool: Funken erzeugen, entnehmen, zurückgeben und zerstören, mit höchstens 5 Funken auf Vorrat
    }

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

    // Ein Funke, dessen Lebensdauer abgelaufen ist
    public void Return(Spark spark)
    {
        Destroy(spark.gameObject);
    }

    // Nach außen und oben, gleichmäßig um den Launcher verteilt
    Vector3 Direction(int i)
    {
        return Quaternion.Euler(0f, i * 360f / sparks, 0f) * new Vector3(0f, 1f, 3f);
    }
}

Alle Lektionen in Spielarchitektur in Unity

Übe selbstständig: Unity-Playground