Sistema de Spawn y Object Pooling

Un servicio de spawn de entidades que abstrae el pooling de objetos detrás de handles opacos para que la lógica de juego nunca toque la instanciación del motor directamente.

Para qué sirve este sistema

Cada proyecto Unity que genera enemigos, proyectiles o elementos de escena acaba escribiendo el mismo código de pooling. Una cola de GameObjects inactivos, un patrón de préstamo y devolución, llamadas dispersas a Instantiate y Destroy, y el estado del pool viviendo en un campo de MonoBehaviour que nadie gestiona del todo. Funciona hasta que deja de hacerlo.

El Game Spawner de Serenity mueve todo eso detrás de una interfaz de servicio tipada. La lógica de juego llama a Spawn y Despawn sobre tipos lógicos. El pool, la referencia al prefab y la instanciación del motor viven en infraestructura, que es donde corresponde.

El problema en Unity

Instantiate y Destroy son llamadas al motor. Cuando la lógica de juego las llama directamente asume una responsabilidad que no debería tener: saber qué prefab usar, decidir si existe un pool, rastrear instancias activas y limpiar al cambiar de escena. Ese acoplamiento hace que la lógica de spawn sea difícil de probar y fácil de romper cuando cambia la implementación del pool.

Las bibliotecas de object pooling resuelven el problema de rendimiento pero no el de arquitectura. El pool sigue siendo un objeto concreto del motor que la lógica de juego debe referenciar. Cambiar la estrategia de pooling — pasar de una cola simple a una implementación por cubo por tipo — sigue requiriendo cambios en los consumidores.

Cómo lo aborda Serenity

La lógica de juego interactúa con IGameSpawnerService, que expone Spawn, Despawn, DespawnAll e IsActive. Spawn recibe un value object SpawnType y devuelve un SpawnHandle — un struct opaco que identifica la instancia mediante un GameEntityId sin exponer ningún tipo del motor. El handle es lo único que guarda la lógica de juego. Despawn devuelve el handle y el pool decide qué hacer con él.

ISpawnFactory es el puerto de aplicación que implementa la infraestructura. Gestiona las llamadas de motor Create, Destroy y DestroyAll. El pooling se gestiona a través de entidades PoolBucket en el dominio, que agrupan handles activos y disponibles por SpawnType. PoolStateSnapshot expone ActiveCount, AvailableCount y TotalCount para diagnóstico sin dar a los consumidores acceso a los internos del pool.

Cómo encaja en Serenity

Game Spawner vive en el namespace Serenity.GameSpawner y sigue la estructura por capas de la foundation. La capa de Dominio define SpawnType, SpawnHandle, PoolStateSnapshot y la entidad PoolBucket. La capa de Aplicación expone IGameSpawnerService y el puerto ISpawnFactory. La capa de Infraestructura proporciona UnityGameSpawnerService y UnitySpawnFactory, que gestionan la instanciación de prefabs y la cola de pooling real. La instalación conecta todo a través de GameSpawnerInstaller y UnityGameSpawnerInstaller.

Game Spawner coopera con los sistemas Wave y Stage para el spawn en lotes temporizados, con el sistema Character para que los personajes generados reciban su contexto de inicialización, con el sistema Rail para el posicionamiento de spawn vinculado a carriles, y con el sistema Game Session para la limpieza de ámbito de sesión mediante DespawnAll.

Flujo de trabajo práctico

  1. Define un SpawnType para cada categoría lógica de entidad que necesite el proyecto, como enemigos, proyectiles o coleccionables.
  2. Registra prefabs y tamaños de pool en la configuración del instalador para que la infraestructura de Unity sepa qué crear.
  3. Inyecta IGameSpawnerService en cualquier clase de capa de aplicación que necesite generar o eliminar entidades.
  4. Llama a Spawn con un SpawnType para obtener un SpawnHandle; conserva el handle para rastrear la instancia.
  5. Llama a Despawn con el handle cuando la entidad ya no sea necesaria; el pool se encarga del resto.
  6. Llama a GetPoolState con un SpawnType para leer un PoolStateSnapshot con fines de diagnóstico o ajuste.

Qué incluye

  • IGameSpawnerService con Spawn, Despawn, DespawnAll e IsActive
  • SpawnHandle — struct opaco que combina GameEntityId y SpawnType para el rastreo seguro de instancias
  • SpawnType — value object inmutable que identifica una categoría lógica de entidad mediante una clave de cadena
  • Entidad de dominio PoolBucket que gestiona handles activos y disponibles por tipo
  • Value object PoolStateSnapshot con ActiveCount, AvailableCount y TotalCount para diagnóstico
  • Puerto de aplicación ISpawnFactory que mantiene la instanciación del motor fuera de la capa de negocio
  • UnitySpawnFactory y UnityGameSpawnerService como implementaciones de infraestructura listas para usar
  • Sobrecargas de DespawnAll para limpieza específica por tipo y limpieza total de sesión

Cuándo usarlo

  • Proyectos que generan entidades repetitivas como enemigos, balas o efectos y necesitan reutilización controlada de objetos.
  • Juegos en los que la lógica de juego debe permanecer desacoplada de las referencias a prefabs de Unity y los detalles de instanciación.
  • Bases de código que necesitan referencias de entidad trazables mediante handles en lugar de campos de GameObject dispersos.
  • Proyectos que se integran con los sistemas Wave, Stage o Game Session y necesitan un despawn masivo coordinado.

Sistemas relacionados

Usa Serenity cuando quieras un spawn y pooling que la lógica de juego pueda invocar sin saber nada sobre GameObjects, prefabs ni colas de pool, y aun así obtener visibilidad de diagnóstico completa mediante snapshots de estado del pool.

Volver a la página principal