ObjectPool w Unity
Część sekcji Architektura gier w Unity ścieżki Unity w Coddy. Lekcja 39 z 50.
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łąb 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 zastaje pustą pulę; actionOnGet jest wywoływana przy każdym Get, a actionOnRelease przy każdym Release. Gdy kłąb zostanie zwolniony, a maxSize zapasowych obiektów już czeka w puli, pula go nie zachowuje: actionOnDestroy go niszczy. defaultCapacity określa jedynie początkowy rozmiar listy puli, więc żadne obiekty nie są tworzone z góry. CountAll zlicza kłęby utworzone przez pulę, które nadal do niej należą, CountActive te znajdujące się w grze, a CountInactive zapasowe. Get włącza kłąb, zanim komin go umieści, co w tym przypadku jest w porządku, ponieważ Puff nie ma OnEnable, które odczytuje jego pozycję. Kłąb przechowuje pulę jako IObjectPool<Puff>, czyli 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 przeszukiwane są zapasowe obiekty; 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.
Wyzwanie
ŚredniFajerwerki. Każde naciśnięcie spacji wystrzeliwuje z wyrzutni 8 iskier, a każda Spark wywołuje Return(this) wyrzutni, gdy upłynie jej 1 sekunda życia (Spark jest gotowa). Obecnie wyrzutnia tworzy i niszczy każdą iskrę. Użyj puli Unity:
- W
Launcher.csutwórz w Awake pulęObjectPool<Spark>. Tworzy ona iskrę przez utworzenie instancjisparkPrefab, włącza iskrę, gdy jest pobierana z puli, i wyłącza ją, gdy do niej wraca, a także niszczy obiekt iskry, gdy ją zwalnia. - Pula przechowuje maksymalnie 5 zapasowych iskier, więc po wystrzeleniu 8, 5 czeka, a 3 zostają zniszczone.
- Spacja pobiera 8 iskier z puli zamiast tworzyć ich instancje, a
Returnzwalnia iskrę do puli zamiast ją niszczyć.
Naciśnij Play, wystrzel kilka serii, a następnie naciśnij Check.
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 najwyżej 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);
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Architektura gier w Unity
1Komponenty, nie hierarchie
Małe komponentyPodstawowy skrypt przeciwnikaKomponenty abstrakcyjneWspółpracujące komponentyPodsumowanie: modułowa wieżyczka4Dane serializowane
Klasy serializowalneListy w InspectorzeWyliczenia w InspectorzePodsumowanie: tabela łupów10Projekt: Arena przetrwania
BohaterTypy wrogów2Interfejsy w Unity
IDamageableInterfejsy i TryGetComponentGetComponentsPodsumowanie: klawisz interakcji5ScriptableObjects
Zasoby danychStatystyki broniWspółdzielone ustawieniaFale jako danePowtórka: baza danych przedmiotów8Kod wielokrotnego użytku
Metody rozszerzająceGeneryczny singletonGeneryczne metody pomocniczePodsumowanie: FindNearest3Zdarzenia
Zdarzenia C# w UnitySubskrypcja w OnEnablePola UnityEventZdarzenia sterują interfejsemPodsumowanie: wyskakujące okno osiągnięcia6Zapisywanie za pomocą JSON
JsonUtilityZapisz gręWczytywanie i wartości domyślnePowtórka: sloty zapisu9Pooling obiektów
Dlaczego używać poolingu obiektówPool z kolejkąObjectPool w UnityPodsumowanie: pula pocisków