Comunicación basada en eventos en un motor de juego: ¿Sí o no?

62

Estoy leyendo Game Coding Complete y el autor recomienda la comunicación dirigida por eventos entre los objetos y módulos del juego.

Básicamente, todos los actores de los juegos vivos deberían comunicarse con los módulos clave (Física, IA, Game Logic, Game View, etc.) a través de un sistema interno de mensajes de eventos. Esto significa tener que diseñar un administrador de eventos eficiente. Un sistema mal diseñado consume ciclos de CPU, que afectan especialmente a las plataformas móviles.

¿Es este un enfoque probado y recomendado? ¿Cómo debo decidir si usarlo?

Bunkai.Satori
fuente
Hola James, gracias por tu respuesta. Me gusta, aunque el libro describe la comunicación dirigida por eventos como un sistema realmente complejo que aparentemente consume una gran cantidad de recursos de la CPU.
Bunkai.Satori
Póngalo en una respuesta y agregue un enlace a una descripción general de alto nivel de un sistema de mensajería de eventos que di como respuesta en el desbordamiento de pila. ¡Disfrutar! Solo recuerda que lo que algunas personas consideran complejo no tiene que ser :)
James
Puede verificar esta respuesta también bien usando c ++ 11: stackoverflow.com/a/22703242/2929762
user2826084
libuvRecientemente ha surgido como una exitosa biblioteca de eventos. (Está en C. Node.js es su caso de uso más famoso.)
Anko

Respuestas:

46

Esta es una expansión de mi comentario a una respuesta completa, como se sugiere.

, simple y llanamente. La comunicación debe suceder y, aunque hay situaciones en las que "¿Ya llegamos?" -tipo de sondeo, hacer que las cosas verifiquen para ver si deberían estar haciendo otra cosa generalmente desperdicia tiempo. En su lugar, podría hacer que reaccionen a las cosas que se les dice que hagan. Además, una vía de comunicación bien definida entre objetos / sistemas / módulos aumenta significativamente las configuraciones paralelas.

He presentado una descripción general de alto nivel de un sistema de mensajería de eventos en Stack Overflow . Lo he usado desde la escuela en títulos de juegos profesionales y ahora en productos que no son de juego, adaptados al caso de uso cada vez, por supuesto.

EDITAR : para responder una pregunta de comentario sobre cómo sabe qué objetos deben recibir el mensaje : los propios objetos deben Requestser notificados sobre los eventos. Su EventMessagingSystem(EMS) necesitará una Register(int iEventId, IEventMessagingSystem * pOjbect, (EMSCallback)fpMethodToUseForACallback)coincidencia Unregister(haciendo una entrada única para un iEventIdpuntero y devolución de llamada fuera del objeto). De esta manera, cuando un objeto quiere saber acerca de un mensaje, puede hacerlo Register()con el sistema. Cuando ya no necesita saber sobre los eventos, puedeUnregister(). Claramente, desearía un grupo de estos objetos de registro de devolución de llamada y una forma efectiva de agregarlos / eliminarlos de las listas. (Por lo general, he usado matrices de pedido automático; una forma elegante de decir que rastrean sus propias asignaciones entre una pila de agrupación de objetos no utilizados y matrices que cambian sus tamaños en su lugar cuando es necesario).

EDITAR : para un juego completamente funcional con un ciclo de actualización y un sistema de mensajería de eventos, es posible que desee ver un proyecto mío de la vieja escuela . La publicación de desbordamiento de pila vinculada anteriormente también se refiere a ella.

James
fuente
Hola James, como estoy pensando más en el sistema interno de mensajería de eventos, creo que no es necesario agregar más costos al motor. Por ejemplo, si hay 50 objetos en la pantalla, pero solo 5 de ellos deben cambiar su comportamiento; Según el sistema tradicional, todos los 50 objetos tendrían que verificar todas sus posibles acciones, para ver si deberían hacer algo. Sin embargo, con el uso de mensajes de eventos, solo se enviarán 5 mensajes a esos 5 objetos con un cambio de acción específico. Eso parece un enfoque que ahorra tiempo.
Bunkai.Satori
La forma en que funciona el sistema vinculado en mi respuesta anterior es que los objetos solo se registran para escuchar los mensajes que desean. Lo usaríamos para nuestro beneficio en los videojuegos donde puede haber entre 100 y 200 objetos en un nivel, pero usted solo 'active' con las que el jugador puede interactuar directamente, manteniendo el número de cosas escuchando en aproximadamente 10 más o menos. En general, este tipo de sistema debería ser 'más inteligente' que un '¿ya llegamos?' sistema de votación, y debe reducir la sobrecarga de un motor, al menos en lo que respecta a la comunicación.
James
Hay una cosa relacionada con el sistema controlado por eventos que me confunde: bajo el sistema tradicional donde cada objeto tiene un conjunto predefinido de métodos (OnUpdate (), OnMove (), OnAI (), OnCollisionCheck () ...) llamado regularmente, cada objeto es responsable de verificar y administrar su estado. Bajo el sistema controlado por eventos, debe haber alguna condición de control del sistema maestro de cada objeto, y luego enviar mensajes a aquellos donde se detectó algún evento. Para mí, esto estandariza los posibles estados de objeto y limita la libertad de crear un comportamiento de objeto único. ¿Es eso cierto?
Bunkai.Satori
El patrón de observador es la alternativa típica al sondeo. en.wikipedia.org/wiki/Observer_pattern
Ricket
1
@ hamlin11 Me alegra que esta información siga ayudando a las personas :) En lo que respecta al registro, si esto sucede bastante, recuerde que querrá optimizarlo para la velocidad, tener un grupo de objetos de registro para extraer, etc.
James
22

Mi opinión es que deberías comenzar a hacer tu juego e implementar aquello con lo que te sientas cómodo. Mientras lo hace, se encontrará utilizando MVC en algunos lugares, pero no en otros; eventos en algunos lugares, pero no en otros; componentes algunos lugares, pero herencia otros; diseño limpio en algunos lugares, y diseño rudo en otros.

Y está bien, porque cuando termines, tendrás un juego, y un juego es mucho más genial que un sistema de transmisión de mensajes.

usuario744
fuente
2
Hola Joe, gracias por tu respuesta, me gusta. Usted cree que no hay necesidad de impulsar ningún enfoque de forma artificial. Debería diseñar aplicaciones como creo que funcionaría, y si algo no funciona más tarde, simplemente lo rehaceré.
Bunkai.Satori
Por eso existe el sistema. Muchas personas han hecho lo que has dicho, presenciado la pesadilla y han dicho que tiene que haber una mejor manera. Puede repetir sus errores o aprender de ellos y quizás avanzar más.
user441521
16

Una mejor pregunta es, ¿qué alternativas hay? En un sistema tan complejo con módulos adecuadamente divididos para física, inteligencia artificial, etc., ¿de qué otra manera puede orquestar estos sistemas?

La transmisión de mensajes parece ser la "mejor" solución a este problema. No puedo pensar en alternativas en este momento. Pero hay muchos ejemplos de mensajes que pasan en la práctica. De hecho, los sistemas operativos utilizan el paso de mensajes para varias de sus funciones. De Wikipedia :

Los mensajes también se usan comúnmente en el mismo sentido como un medio de comunicación entre procesos; la otra técnica común son los flujos o tuberías, en los que los datos se envían como una secuencia de elementos de datos elementales (la versión de nivel superior de un circuito virtual).

(La comunicación entre procesos es la comunicación entre procesos (es decir, ejecución de instancias de programas) dentro del entorno del sistema operativo)

Entonces, si es lo suficientemente bueno para un sistema operativo, probablemente sea lo suficientemente bueno para un juego, ¿verdad? También hay otros beneficios, pero dejaré que el artículo de Wikipedia sobre la transmisión de mensajes explique.

También hice una pregunta sobre Stack Overflow, "¿Estructuras de datos para pasar mensajes dentro de un programa?" , que es posible que desee leer de nuevo.

Ricket
fuente
Hola Ricket, gracias por tu respuesta. Usted preguntó qué otras formas podrían usarse para permitir que los objetos del juego se comuniquen entre sí. Lo que viene a mi mente son las llamadas a métodos directos. Es esencial que no le brinde tanta flexibilidad, pero por otro lado, evita la generación de mensajes de eventos, pases, lecturas, etc. Todavía hablamos de plataformas móviles, no de juegos de alta gama. El sistema operativo utiliza mucho el sistema de mensajería interna. Sin embargo, no está diseñado para ejecutarse en tiempo real, donde incluso pequeños retrasos causan interrupciones.
Bunkai.Satori
Déjame mantener esta pregunta abierta por un tiempo. Me gustaría reunir más opiniones, antes de cerrarlo.
Bunkai.Satori
Las llamadas a métodos directos generalmente dan como resultado el acoplamiento de las clases que se llaman entre sí. Esto no es bueno para un sistema separado en componentes que se supone que son intercambiables. Hay algunos patrones que pueden facilitar las llamadas de método directo con menos cohesión (por ejemplo, la parte "controlador" de MVC básicamente sirve para facilitar las consultas de vista del modelo, a menos que las acople), pero en general, la transmisión de mensajes es la única forma de un sistema para tener cero acoplamiento entre subsistemas (o al menos la única forma que conozco).
Ricket
Ah, así que ahora comenzamos a discutir patrones. He escuchado un poco sobre este enfoque de programación. Ahora, empiezo a entender qué son los patrones. Es completamente diferente mirar el diseño de la aplicación. Usted controla los objetos de forma masiva, mientras usa el mismo código para verificar cada objeto. Después de verificar el objeto, establece sus atributos en consecuencia.
Bunkai.Satori
1
La pregunta "Estructuras de datos ..." tiene una respuesta INCREÍBLE. Diría que su respuesta seleccionada y el artículo Nebula3 adjunto son la mejor descripción de la arquitectura del motor de juego de su tamaño que he visto.
deft_code el
15

Los mensajes generalmente funcionan bien cuando:

  1. Lo que envía el mensaje no le importa si se recibe.
  2. El remitente no necesita recibir una respuesta inmediata del receptor.
  3. Puede haber múltiples receptores escuchando a un solo remitente.
  4. Los mensajes se enviarán con poca frecuencia o de forma impredecible. (En otras palabras, si cada objeto necesita recibir un mensaje de "actualización" en cada cuadro, los mensajes realmente no te están comprando mucho).

Cuanto más coincidan sus necesidades con esa lista, mejores serán los mensajes adecuados. A un nivel muy general, son bastante buenos. Hacen un buen trabajo al no desperdiciar los ciclos de la CPU solo para no hacer nada a diferencia del sondeo u otras soluciones más globales. Son fantásticos para desacoplar partes de una base de código, lo que siempre es útil.

munificente
fuente
4

Grandes respuestas hasta ahora de James y Ricket, solo respondo para agregar una nota de precaución. La comunicación impulsada por mensajes / eventos es ciertamente una herramienta importante en el arsenal del desarrollador, pero se puede usar en exceso fácilmente. Cuando tienes un martillo, todo parece un clavo, y así sucesivamente.

Absolutamente, 100%, debe pensar en el flujo de datos alrededor de su título y ser plenamente consciente de cómo la información pasa de un subsistema a otro. En algunos casos, pasar mensajes es claramente mejor; en otros, puede ser más apropiado que un subsistema opere sobre una lista de objetos compartidos, permitiendo que otros subsistemas también operen en la misma lista compartida; y muchos más métodos además.

En todo momento debe saber qué subsistemas necesitan acceso a qué datos y cuándo deben acceder a ellos. Esto afecta su capacidad de paralelizar y optimizar, además de ayudarlo a evitar los problemas más insidiosos cuando diferentes partes de su motor se entrelazan demasiado. Debe estar pensando en minimizar la copia innecesaria de datos y en cómo su diseño de datos afecta su uso de caché. Todo lo cual lo guiará a la mejor solución en cada caso.

Dicho esto, casi todos los juegos en los que he trabajado han tenido un sólido sistema de notificación de eventos / pases de mensajes asíncronos. Le permite escribir código sucinto y eficiente que se pueda mantener, incluso ante necesidades de diseño complejas y que cambian rápidamente.

MrCranky
fuente
+1 para buena información adicional. Hola, MrCranky, gracias. Según lo que diga, valdría la pena considerar el sistema de mensajería de eventos incluso para juegos móviles. Sin embargo, la planificación no debe pasarse por alto y debe considerarse muy bien dónde se utilizará el sistema de mensajería.
Bunkai.Satori
4

Bueno, sé que esta publicación es bastante antigua, pero no pude resistirme.

Recientemente construí un motor de juego. Utiliza bibliotecas de fiestas en 3D para renderizar y física, pero escribí la parte central, que define y procesa las entidades y la lógica del juego.

El motor seguramente sigue un enfoque tradicional. Hay un ciclo de actualización principal que llama a la función de actualización para todas las entidades. Las colisiones se informan directamente por devolución de llamada en las entidades. Las comunicaciones entre entidades se realizan utilizando punteros inteligentes intercambiados entre entidades.

Hay un sistema de mensajes primitivo, que procesa solo un pequeño grupo de entidades para generar mensajes. Es preferible que esos mensajes se procesen al final de una interacción del juego (por ejemplo, la creación o destrucción de una entidad) porque pueden interferir con la lista de actualizaciones. Entonces, al final de cada ciclo del juego, se consume una pequeña lista de mensajes.

A pesar del sistema de mensajes primitivo, diría que el sistema está en gran medida "basado en bucles de actualización".

Bien. Después de usar este sistema, creo que es muy simple, rápido y bien organizado. La lógica del juego es visible y autónoma dentro de las entidades, no dinámica como un mensaje quewe. Realmente no lo haría impulsado por eventos porque, en mi opinión, los sistemas de eventos introducen una complejidad innecesaria en la lógica del juego y hacen que el código del juego sea muy difícil de entender y depurar.

Pero también creo que un sistema puro "basado en bucles de actualización" como el mío también tiene algunos problemas.

Por ejemplo, en algunos momentos, una entidad puede estar en un "estado de no hacer nada", puede estar esperando que el jugador se acerque o algo más. En la mayoría de esos casos, la entidad quema el tiempo del procesador por nada y es mejor apagar la entidad y encenderla cuando ocurre un determinado evento.

Entonces, en mi próximo motor de juego, voy a adoptar un enfoque diferente. Las entidades se registrarán para las operaciones del motor, como actualizaciones, dibujos, detección de colisiones, etc. Cada uno de estos eventos tendrá listas separadas de interfaces de entidad para las entidades reales.

Artur Correa
fuente
Encontrarás que las entidades se convierten en bases de códigos de monstruos que se vuelven difíciles de manejar con cualquier tipo de complejidad en el juego. Terminará uniéndolas y apestará agregar o cambiar características. Al dividir sus funciones en componentes pequeños, será más fácil mantener y agregar funciones. Entonces la pregunta es cómo se comunican estos componentes. La respuesta son eventos de un componente que generan funciones de otro mientras pasan datos primitivos a lo largo de los argumentos.
user441521
3

Si. Es una forma muy eficiente para que los sistemas de juego se comuniquen entre sí. Los eventos lo ayudan a desacoplar muchos sistemas y permiten incluso compilar cosas por separado sin conocer la existencia de los demás. Esto significa que sus clases pueden crear prototipos más fácilmente y los tiempos de compilación son más rápidos. Más importante aún, terminas con un diseño de código plano en lugar de un desastre de dependencia.

Otro gran beneficio de los eventos es que se transmiten fácilmente a través de una red u otro canal de texto. Puede grabarlos para su posterior reproducción. Las posibilidades son infinitas.

Otro beneficio: puede tener varios subsistemas escuchando el mismo evento. Por ejemplo, todas sus vistas remotas (jugadores) pueden suscribirse automáticamente al evento de creación de entidades y generar entidades en cada cliente con poco trabajo de su parte. Imagine cuánto más trabajo sería si no utilizara los eventos: tendría que hacer Update()llamadas en algún lugar o tal vez llamar view->CreateEntitydesde la lógica del Juego (donde el conocimiento sobre la vista y la información que requiere no pertenecen). Es difícil resolver ese problema sin eventos.

Con los eventos, obtienes una solución elegante y desacoplada que admite un número infinito de objetos de tipos ilimitados que pueden suscribirse a los eventos y hacer lo suyo cuando algo sucede en tu juego. Por eso los eventos son geniales.

Más detalles y una implementación aquí .

usuario2826084
fuente
Grandes puntos, aunque unilaterales. Siempre sospecho de la generalidad: ¿hay casos contra el uso de eventos?
Anko
1
Puntos anti-uso: cuando absolutamente debe tener una respuesta directa. Cuando lo que quieras hacer en algún evento siempre se ejecuta directamente y en el mismo hilo. Cuando no hay absolutamente ningún retraso externo que pueda retrasar la finalización de las llamadas. Cuando solo tienes dependencias lineales. Cuando rara vez haces varias cosas al mismo tiempo. Si observa node.js, todas las llamadas io se basan en eventos. Node.js es un lugar donde los eventos se han implementado 100% correctamente.
usuario2826084
El sistema de eventos no necesita ser asíncrono. Puedes usar corutinas para simular asíncrono, mientras obtienes sincronización cuando la necesites.
user441521
2

Por lo que he visto, no parece ser muy común tener un motor completamente basado en mensajes. Por supuesto, hay subsistemas que se prestan muy bien a dicho sistema de mensajería, como redes, GUI y posiblemente otros. Sin embargo, en general, hay algunos problemas en los que puedo pensar:

  • Los motores de juego manejan grandes cantidades de datos.
  • Los juegos deben ser rápidos (20-30 FPS debería ser el mínimo).
  • A menudo es necesario saber si se ha hecho algo o cuándo se hará.

Con el enfoque común de los desarrolladores de juegos de "tiene que ser lo más eficiente posible", este sistema de mensajería no es tan común.

Sin embargo, estoy de acuerdo en que debes seguir adelante y probarlo. Ciertamente, hay muchas ventajas con este sistema y la potencia de cómputo está disponible a bajo precio hoy en día.

mrbinary
fuente
No sé si estoy de acuerdo con esto. La alternativa es el sondeo, que es un desperdicio. Responder solo a los eventos es lo más eficiente. Sí, todavía habrá algunas encuestas que sucederán y generarán eventos, pero también hay un buen número de cosas que están orientadas a eventos.
user441521
No estoy de acuerdo con esto tampoco. La cantidad de datos que manejan los motores de juegos de datos es en gran medida irrelevante, ya que la mensajería de eventos está diseñada para la comunicación subsistema-subsistema. Los objetos del juego no deben comunicarse directamente con otros objetos del juego (aunque los controladores de eventos personalizados en los objetos podrían ser una excepción). Debido a este número relativamente pequeño de subsistemas y sus áreas de "interés", el costo de rendimiento de una red troncal de mensajería bien diseñada en un motor de subprocesos múltiples es insignificante.
Ian Young