Menu
CoddyTech

Unitys ObjectPool

Teil des Abschnitts Spielarchitektur in Unity der Unity-Journey von Coddy. Lektion 39 von 50.

Unity enthält einen Pool, ObjectPool<T> in UnityEngine.Pool. Du legst mit vier Lambdas fest, was bei jedem Schritt passieren soll, und der Pool verwaltet die Objekte und ihre Anzahl. Dieser Schornstein stößt alle 0.2 Sekunden eine Rauchwolke aus, und jede Wolke 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 Objekte im Pool warten, behält der Pool sie nicht: actionOnDestroy zerstört sie. defaultCapacity ist lediglich die anfängliche Größe der Liste des Pools, es wird also nichts im Voraus erstellt. CountAll zählt die Rauchwolken, die der Pool erstellt hat und noch besitzt, CountActive diejenigen, die sich im Spiel befinden, und CountInactive die übrigen Objekte. Get aktiviert die Rauchwolke, bevor der Schornstein sie positioniert. Das ist hier in Ordnung, weil Puff kein OnEnable hat, das ihre Position ausliest. Die Rauchwolke speichert den Pool als IObjectPool<Puff>, der Schnittstelle, die jeder Unity-Pool implementiert, und Launch setzt ihr Alter bei jeder Verwendung zurück.

Ist collectionCheck aktiviert, löst die Freigabe einer Rauchwolke, die sich bereits im Pool befindet, 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, da sie bei jedem Release die übrigen Objekte 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 deaktiviert actionOnRelease es.

challenge icon

Aufgabe

Mittel

Feuerwerk. Bei jedem Druck auf die Leertaste schießen 8 Funken aus dem Abschussgerät, und jeder Spark ruft Return(this) des Abschussgeräts auf, wenn seine Lebensdauer von 1 Sekunde abgelaufen ist (Spark ist bereit). Heute instanziiert und zerstört das Abschussgerät jeden Funken. Verwende Unitys Pool:

  • Erstelle in Launcher.cs in Awake einen ObjectPool<Spark>. Er erstellt einen Funken, indem er sparkPrefab instanziiert, aktiviert einen Funken, wenn er entnommen wird, und deaktiviert ihn, wenn er zurückkommt. Wenn ein Funke freigegeben wird, zerstört er dessen Objekt.
  • Der Pool behält höchstens 5 Ersatzfunken, also warten nach einem Ausbruch von 8 Funken 5, und 3 werden zerstört.
  • Mit der Leertaste werden die 8 Funken aus dem Pool entnommen, anstatt sie zu instanziieren, und Return gibt den Funken an den Pool zurück, anstatt ihn zu zerstören.

Drücke Play, löse einige Ausbrüche aus und klicke dann auf Check.

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: Erzeugen, Entnehmen, Zurückgeben und Zerstören, mit höchstens 5 ungenutzten Funken
    }

    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);
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Spielarchitektur in Unity