Hasta ahora, los sistemas de componentes de entidad que he usado han funcionado principalmente como el artemis de Java:
- Todos los datos en componentes
- Sistemas independientes sin estado (al menos en la medida en que no requieren entrada en la inicialización) iterando sobre cada entidad que contiene solo los componentes en los que está interesado un sistema en particular
- Todos los sistemas procesan sus entidades una marca, luego todo comienza de nuevo.
Ahora estoy tratando de aplicar esto a un juego por turnos por primera vez, con toneladas de eventos y respuestas que deben ocurrir en un orden establecido en relación entre sí, antes de que el juego pueda seguir adelante. Un ejemplo:
El jugador A recibe daño de una espada. En respuesta a esto, la armadura de A entra en acción y reduce el daño recibido. La velocidad de movimiento de A también se reduce como resultado de debilitarse.
- El daño recibido es lo que desencadena toda la interacción.
- La armadura debe calcularse y aplicarse al daño entrante antes de que el daño se aplique al jugador
- La reducción de la velocidad de movimiento no se puede aplicar a una unidad hasta después de que el daño se haya infligido, ya que depende de la cantidad de daño final.
Los eventos también pueden desencadenar otros eventos. La reducción del daño de la espada con armadura puede hacer que la espada se rompa (esto debe ocurrir antes de que se complete la reducción del daño), lo que a su vez puede causar eventos adicionales en respuesta a ella, esencialmente una evaluación recursiva de los eventos.
Con todo, esto parece conducir a algunos problemas:
- Muchos ciclos de procesamiento desperdiciados: la mayoría de los sistemas (salvo las cosas que siempre se ejecutan, como el renderizado) simplemente no tienen nada que valga la pena hacer cuando no es "su turno" para trabajar, y pasan la mayor parte del tiempo esperando que el juego entre Un estado de trabajo válido. Esto ensucia cada uno de esos sistemas con controles que siguen creciendo en tamaño a medida que se agregan más estados al juego.
- Para averiguar si un sistema puede procesar entidades que están presentes en el juego, necesitan alguna forma de monitorear otros estados de entidad / sistema no relacionados (el sistema responsable del daño debe saber si la armadura se ha aplicado o no). Esto confunde los sistemas con múltiples responsabilidades o crea la necesidad de sistemas adicionales sin otro propósito que escanear la colección de entidades después de cada ciclo de procesamiento y comunicarse con un conjunto de oyentes diciéndoles cuándo está bien hacer algo.
Los dos puntos anteriores suponen que los sistemas funcionan en el mismo conjunto de entidades, que terminan cambiando de estado usando banderas en sus componentes.
Otra forma de resolverlo sería agregar / eliminar componentes (o crear entidades completamente nuevas) como resultado de un solo trabajo de sistemas para progresar en el estado de los juegos. Esto significa que siempre que un sistema tenga una entidad coincidente, sabe que puede procesarlo.
Sin embargo, esto hace que los sistemas sean responsables de activar sistemas posteriores, lo que dificulta razonar sobre el comportamiento de los programas, ya que los errores no se mostrarán como resultado de una sola interacción del sistema. Agregar nuevos sistemas también se vuelve más difícil ya que no se pueden implementar sin saber exactamente cómo afectan a otros sistemas (y es posible que los sistemas anteriores tengan que modificarse para activar los estados en los que está interesado el nuevo sistema), lo que frustra el propósito de tener sistemas separados con una sola tarea
¿Es algo con lo que tendré que vivir? Todos los ejemplos de ECS que he visto han sido en tiempo real, y es realmente fácil ver cómo funciona este bucle de una iteración por juego en tales casos. Y todavía lo necesito para renderizar, simplemente parece no apto para sistemas que pausan la mayoría de los aspectos de sí mismos cada vez que sucede algo.
¿Hay algún patrón de diseño para mover el estado del juego hacia adelante que sea adecuado para esto, o debería simplemente sacar toda la lógica del bucle y, en su lugar, activarla solo cuando sea necesario?