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.
Aufgabe
MittelLass das Feuerwerk aus Unitys Pool explodieren.
- Erstelle in
AwakevonLaunchereinenObjectPool<Spark>: Erzeuge Objekte mitInstantiate(sparkPrefab), aktiviere sie beim Abrufen, deaktiviere sie beim Freigeben und zerstöre das Objekt beim Freigeben; setzemaxSize: 5. - Rufe in
Updatejeden Funken mitpool.Get()ab, stattInstantiatezu verwenden. - Rufe in
Returnpool.Release(spark)auf, stattDestroyzu 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
1Komponenten statt Hierarchien
Kleine KomponentenEin Basis-GegnerskriptAbstrakte KomponentenZusammenwirkende KomponentenRückblick: Modulares GeschützRückblick: Zwillingskanone4Serialisierte Daten
Serialisierbare KlassenListen im InspectorEnums im InspectorRückblick: Loot-TabelleRückblick: Beute auswürfeln2Schnittstellen in Unity
IDamageableSchnittstellen und TryGetComponentGetComponentsZusammenfassung: InteraktionstasteZusammenfassung: Nächstes in Reichweite5ScriptableObjects
Daten-AssetsWaffenwerteGemeinsame EinstellungenDen Wind speichernWellen als DatenRückblick: Item-DatenbankRückblick: Im Shop einkaufen8Wiederverwendbarer Code
ErweiterungsmethodenEin generisches SingletonGenerische HilfsmethodenRückblick: FindNearest3Ereignisse
C#-Ereignisse in UnityIn OnEnable abonnierenUnityEvent-FelderEreignisse steuern die UIRückblick: Erfolgs-PopupRückblick: Popup-Timer6Speichern mit JSON
JsonUtilityDas Spiel speichernLaden und StandardwerteRückblick: SpeicherplätzeSpeicherplatz auswählen und löschen9Objekt-Pooling
Warum Objekte poolenEin Pool mit einer WarteschlangeUnitys ObjectPoolRückblick: Geschoss-PoolÜbe selbstständig: Unity-Playground