ObjectPool de Unity
Parte de la sección Arquitectura de juegos en Unity del Journey de Unity de Coddy. Lección 48 de 64.
Unity incluye un pool, ObjectPool<T> en UnityEngine.Pool. Le indicas qué hacer en cada paso con cuatro lambdas, y se encarga de los objetos de reserva y los recuentos. Esta chimenea expulsa humo cada 0.2 segundos, y cada bocanada vuelve al pool después de un segundo:
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 se ejecuta cuando Get encuentra el pool vacío; actionOnGet se ejecuta en cada Get y actionOnRelease en cada Release. Cuando se devuelve una bocanada y ya hay maxSize objetos de reserva en el pool, este no la conserva: actionOnDestroy la destruye. defaultCapacity solo es el tamaño inicial de la lista del pool, así que no se crea nada de antemano. CountAll cuenta las bocanadas que el pool ha creado y todavía posee, CountActive las que están en el juego y CountInactive las de reserva. Get activa la bocanada antes de que la chimenea la coloque, lo cual está bien en este caso porque Puff no tiene un OnEnable que lea su posición. La bocanada conserva el pool como IObjectPool<Puff>, la interfaz que implementan todos los pools de Unity, y Launch reinicia su edad en cada uso.
Con collectionCheck activado, devolver una bocanada que ya está en el pool genera una InvalidOperationException: Trying to release an object that has already been released to the pool. La comprobación se ejecuta solo en el Editor, porque busca en los objetos de reserva en cada Release; en una compilación, la bocanada quedaría dos veces en el pool y dos llamadas a Get entregarían el mismo objeto. Una causa común es que un objeto siga ejecutándose después de que se devuelva y vuelva a devolverse a sí mismo; por eso actionOnRelease lo desactiva.
Desafío
IntermedioLanza los fuegos artificiales usando el grupo de Unity.
- En
AwakedeLauncher, crea unObjectPool<Spark>: crea conInstantiate(sparkPrefab), actívalo cuando se obtenga, desactívalo cuando se libere, destruye el objeto cuando se suelte y establecemaxSize: 5. - En
Update, obtén cada chispa conpool.Get()en lugar deInstantiate. - En
Return, llama apool.Release(spark)en lugar deDestroy.
Pulsa «Jugar» y lanza unas cuantas tandas. Después, pulsa «Comprobar».
Pruébalo tú mismo
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Pool;
public class Launcher : MonoBehaviour
{
public Spark sparkPrefab;
public int sparks = 8;
void Awake()
{
// Crea el pool: crea, obtiene, libera y destruye, con un máximo de 5 chispas de reserva
}
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 chispa cuya vida ha terminado
public void Return(Spark spark)
{
Destroy(spark.gameObject);
}
// Hacia fuera y hacia arriba, distribuidas uniformemente alrededor del lanzador
Vector3 Direction(int i)
{
return Quaternion.Euler(0f, i * 360f / sparks, 0f) * new Vector3(0f, 1f, 3f);
}
}
Todas las lecciones de Arquitectura de juegos en Unity
1Componentes, no jerarquías
Componentes pequeñosUn script base para enemigosComponentes abstractosComponentes que trabajan juntosRepaso: torreta modularRepaso: cañón doble4Datos serializados
Clases serializablesListas en el InspectorEnumeraciones en el InspectorRepaso: tabla de botínRepaso: sortear el botín2Interfaces en Unity
IDamageableInterfaces y TryGetComponentGetComponentsRepaso: Tecla de interacciónRepaso: El más cercano al alcance5ScriptableObjects
Activos de datosEstadísticas de armasConfiguración compartidaGuardar el vientoOleadas como datosRepaso: base de datos de objetosRepaso: comprar en la tienda3Eventos
Eventos de C# en UnitySuscribirse en OnEnableCampos UnityEventLos eventos controlan la interfazRepaso: ventana emergente de logrosRepaso: temporizador de la ventana emergente6Guardar con JSON
JsonUtilityGuarda la partidaCargar y valores predeterminadosRepaso: espacios de guardadoElegir y eliminar un espacio9Pool de objetos
Por qué usar un pool de objetosUn pool con una colaObjectPool de UnityResumen: pool de balasPractica por tu cuenta: Playground de Unity