Estado del juego y manejo de entradas en sistemas de entidades basados ​​en componentes

16

Mi pregunta es:

¿Cómo puedo manejar los estados de juego en mi sistema de entidades, sin recurrir a mantener una pila de objetos de estado de juego?

Por lo tanto, el diseño de mi sistema de entidad significa que cuando una entidad necesita registrarse para eventos de entrada, por ejemplo, el componente de entrada llama al sistema de entrada y dice "registrar esta entidad para esta entrada". Todo esto está muy bien, sin embargo, si agrega a esto el concepto de estados del juego (digamos una pantalla de pausa), se convierte en un problema determinar si una entidad está en el estado actual y debe recibir la entrada.

Podría aumentar el componente / sistema de entrada para que diga: "registre esta entidad para esta entrada mientras esté en estos estados del juego", pero esto requiere que cada entidad sepa en qué estados se va a usar, y eso puede no ser obvio. Además, mantener una lista de estados de juego por entrada registrada (y otros sistemas que usan devoluciones de llamada) no parece demasiado eficiente.

Otra idea que tuve es que habrá una entidad que representa el estado del juego, márquelo como deshabilitado, luego, al generar el evento de entrada, verifique que la entidad no sea descendiente de una entidad con estado de juego deshabilitado. Parece costoso trabajar con el padre para cada devolución de llamada.

Otra idea es que todos los sistemas almacenen sus datos en clave contra el estado actual, de esa manera, al generar la entrada, la entidad objetivo ni siquiera será candidata. Sin embargo, esto realmente perjudica la capacidad de permitir la comunicación entre entidades en diferentes estados (no es tanto un problema para las pantallas de pausa, sino pensar en la selección de bloqueo en Oblivion / Skyrim).

La única otra idea que he tenido es que todos los componentes manejen los eventos de cambio de estado y se comuniquen con su sistema relevante para deshabilitar todo lo que hayan registrado y volver a habilitarlo al volver a este estado.

El segundo (marcar un objeto como deshabilitado) y el siguiente (hacer que cada componente se ocupe de los cambios de estado) parece ser la mejor de mis ideas, pero ninguna de ellas me llama especialmente la atención.

¿Alguien más tiene alguna otra idea sobre cómo hacer esto?

editar Mientras hablo de entrada específicamente en esta pregunta, puede significar cualquier sistema capaz de enviar mensajes / eventos a entidades, como colisiones, eventos de temporizador, etc.

elFarto
fuente
66
Lo hago así: tengo pantallas, MenuScreen PauseScreen GameScreen, cada pantalla puede crear su propio mundo (contenedor para entidades) y sistemas (como RenderingSystem) y luego en GameScreen creo World, Entity con CameraComponent y configuro CameraComponent.RenderTarget en pantallas de fondo. De esta manera, puedo agregar InventoryScreen que tendrá entidades y sistemas propios (como el renderizador simplificado). La entrada puede ser transmitida de pantalla para el mundo, por lo que su interfase de usuario decidirá si va a pasar a la pantalla de entrada (si es enfocado, visible, etc.) y que pasará a la entrada y entidades del mundo
Kikaimaru
2
@ Byte56 No realmente, solo el primero tiene que ver con los estados de juego (los otros 2 son estados dentro de las entidades), y eso realmente no aborda el mismo problema que estoy teniendo. Cuando el juego está en pausa, algo tiene que pasarle al sistema de entrada para evitar que envíe mensajes de movimiento a la entidad del jugador (por ejemplo), simplemente no puedo encontrar una buena manera de hacer esto.
elFarto
1
OK, considérelos relacionados entonces. Buena pregunta.
MichaelHouse
1
Algo más a tener en cuenta que ha sido una molestia para mis sistemas basados ​​en componentes en el pasado: la interfaz de usuario de múltiples capas. Cuadro de diálogo emergente en la parte superior del mundo o pantallas de varios niveles. Hasta ahora ha aparecido en todos los juegos que he hecho, por lo que diría que me asegure de considerar un enfoque que pueda resolver ese problema.
ADB

Respuestas:

14

Lo que se usa a menudo es un intermedio Intent Systemque abstrae la entrada y realiza un seguimiento del contexto y los estados de juego relevantes.

El sistema Intent dejará de transmitir entradas cuando la simulación esté en pausa, por ejemplo. También maneja el mapeo entre eventos e intenciones del controlador (moverse en dirección, correr, disparar, recargar ...).

De esta manera, sus otros componentes no dependen de entradas / gamepads específicos (BUTTON_A, BUTTON_B vs BUTTON_X, BUTTON_O ...) pero todos reaccionan a los mismos intentos (IntentRun, IntentReload ...).

Otra ventaja es que el sistema de intención puede estar al tanto de los controladores disponibles que se agregan / eliminan, ya que puede enviar intentos a cualquier suscriptor, incluso fuera de la simulación, puede manejar intentos similares AddPlayer(controllerID).

La cantidad de información sobre el estado del juego que proporciona al sistema a través de eventos / mensajes o directamente depende de usted. Pero el tiempo invertido en el sistema de intención generalmente vale la pena.

Puede gestionar los contextos de intención que generarán intenciones cuando estén conectados al sistema.

El contexto puede ser priorizado, es decir:

  • SimulationAvailableContext envía intenciones a la simulación mientras está disponible (pero no en ejecución), por ejemplo, mover la cámara, acercar, alejar, agregar / eliminar reproductor ...
  • SimulationRunningContext envía intenciones a la simulación mientras no está en pausa mover jugador, enviar unidad a posición, disparar ...

De esta manera, puede agregar y eliminar los contextos que actualmente son relevantes.

Y una cosa sobre todos los sistemas de intención es que debe ejecutarse mientras la simulación está en pausa.

Una forma que se usa a menudo para jugar / pausar la simulación del juego sin interrumpir las actualizaciones no relacionadas con la simulación es usar un conjunto diferente de veces. es decir GenericSystem::onTime(Long time, Long deltaTime, Long simTime, Long simDeltaTime).

Con este enfoque, su motor puede simplemente bloquear los incrementos en el simTime de los juegos, lo que a su vez bloqueará las actualizaciones en los motores de animación y física relevantes que se usan al simTime and simDeltaTimetiempo que permite actualizaciones continuas del efecto de resorte de su cámara si tiene que moverse incluso durante la pausa, la animación de el efecto de carga en una cartelera virtual en el juego mientras se descargan los datos ...

Coyote
fuente
Me gusta el hecho de que esto no tiene que llamar a un montón de funciones "Cambios de estado" en todas las entidades. Tienes que preocuparte de que se envíen las intenciones incorrectas en el momento equivocado, pero creo que eso es mejor que la alternativa.
Thomas Marnell
sus entidades pueden ignorar intentos como Saltar, mientras que su estado no les permite saltar (es decir, no tocar el suelo). pero no tienen que preocuparse por recibir esas intenciones mientras el juego está en pausa.
Coyote
Ya había pensado en dejar que la entidad le dijera al sistema de entrada en qué estados entregar mensajes, pero no había pensado en poner los estados en la entrada en sí, lo cual es una buena idea. También es bueno dividir el tiempo y simTime aparte.
elFarto
Debe evitar hinchar su estado relacionado con la simulación con cosas no relacionadas con la simulación. Mueva toda la IU y el código relacionado con el jugador lo más lejos posible de la simulación misma y en la simulación concéntrese solo en los intentos.
Coyote
Hola @ Cooyote, este sistema suena muy interesante. ¿Podría tal vez proporcionar más información respondiendo esta pregunta ? ¡Gracias!
Pek
2

¿Qué tal crear un sistema de eventos global y luego tener un componente de escucha de eventos para cada entidad? Después de un evento "Game State Change", podría jugar con componentes individualmente para cada entidad en particular.

Digamos que tiene un componente de entrada. Después de que el componente de escucha de eventos recibe el evento de cambio de estado del juego, cambia valores muy específicos para ese componente de entrada en particular, por lo que no recibirá ninguna llamada de entrada o no hará ningún movimiento o llamadas de respuesta al sistema o su propietario.

Esto funciona para mí, ya que la mayoría de mis componentes están programados (a través de Lua). Es decir, tengo un componente de entrada, que se dispara una vez cuando se presiona una tecla y se dispara de un movimiento + dirección y luego se dispara cuando se suelta la tecla y se dispara una parada + dirección. También hay un componente de escucha de eventos que contacta con el componente de entrada (si el juego está en pausa) para dejar de disparar cualquier función y detenerse si es necesario. Podría agregar fácilmente otra entidad con una reacción diferente a los mismos eventos y pulsaciones de teclas usando otro script. De esta forma, guardaría la interacción entre diferentes entidades en diferentes estados e incluso la haría mucho más personalizable. Lo que es más, algunas entidades pueden incluso no tener el componente de escucha de eventos en ellas.

Lo que acabo de explicar es básicamente un ejemplo práctico de su cuarta solución.

karmalis
fuente