Los sistemas de eventos son increíbles, hacen un código domador extremadamente difícil de manejar y realmente permiten la creación dinámica de juegos a través de la comunicación fácil de objetos y el bucle del juego. Estoy teniendo dificultades con la eficiencia de mi implementación actual. Actualmente, mi ligera optimización de separar las listas de objetos en los eventos a los que responden ha hecho maravillas, pero debería haber más que pueda hacer.
Actualmente tengo dos métodos:
Más simple: todos los objetos se agregan a un vector cuando se envía un evento a todos los objetos se les envía el evento a través de su método handle_event ()
Más complejo: tengo un mapa con cadena como clave e int como valor. Cuando se agrega un tipo de evento, se agrega a este mapa, con el int simplemente incrementado (debe haber una mejor manera)
el vector de vectores de objetos y luego empuja hacia atrás un nuevo vector para manejar ese tipo de evento.
Cuando se llama a un evento, simplemente llama al int correspondiente en el mapa eventTypes al tipo dentro del vector de vectores de objetos y envía ese evento a cada objeto que maneja ese tipo de evento.
Este primer método es bastante lento (obviamente) para muchos objetos, pero bastante rápido para muy pocos objetos. Mientras que el segundo método es bastante rápido con objetos grandes que desean manejar diferentes tipos de eventos, pero más lento que el primer método por objeto con objetos que manejan el mismo tipo de evento.
¿Hay una manera más rápida (en cuanto al tiempo de ejecución)? ¿Hay una forma más rápida de buscar un int desde el tipo de cadena? (Inicialmente tenía una enumeración, pero no permitía tipos personalizados, que son necesarios debido al nivel de dinamismo deseado).
fuente
Respuestas:
Parece que estás diciendo que el gran cuello de botella de rendimiento está buscando ID de eventos (enteros) de sus nombres de cadena. Podrías preprocesar los datos de tu juego para convertir todos los nombres de eventos en enteros antes de ejecutar el juego, o posiblemente mientras cargas el nivel; entonces no tendrías que hacer ninguna conversión durante el juego.
Si los objetos se crean y destruyen con frecuencia, puede haber mucha rotación en los vectores de los objetos. En este caso, podría beneficiarse utilizando listas vinculadas en lugar de vectores; son más rápidos para la inserción y eliminación.
fuente
EventType takeDamageEvent = EventSystem::CacheEventType("takeDamageEvent");
. Conviértalo en un miembro estático de las clases y solo tendrá una copia flotando en cada clase que lo necesite.Bueno, primero saquemos las cosas simples del camino. Tiene esto
map
entre cadenas (el nombre del evento, presumiblemente) y enteros (el índice de los oyentes de eventos registrados).El tiempo de búsqueda en a
map
se basa en dos cosas: la cantidad de elementos en el mapa y el tiempo que lleva hacer la comparación entre dos claves (ignorando los problemas de caché). Si el tiempo de búsqueda es un problema, una forma de manejarlo es cambiar la función de comparación cambiando el tipo de cadena.Asumamos que estás usando
std::string
yoperator<
para comparación. Esto es altamente ineficiente; hace una comparación byte-wise. No te importa la cadena real menos que la comparación; solo necesita algún tipo de comparación que dé un orden estrictamente débil (porquemap
no funciona de otra manera).Por lo tanto, debe usar una cadena de longitud fija de 32 bytes en lugar de
std::string
. Los uso para identificadores. Las pruebas de comparación para estas cadenas fijas no hacen comparaciones por bytes; hacen comparaciones de 32 bits (o 64 bits) en su lugar. Toma cada 4 bytes como un entero sin signo y lo compara con los 4 bytes correspondientes de la otra cadena. De esa manera, la comparación solo toma como máximo 8 comparaciones. Asegura un orden estrictamente débil, aunque el orden no tiene nada que ver con los datos como caracteres.El almacenamiento de cadenas de más de 31 bytes (necesita el carácter NULL) trunca la cadena (pero desde el medio, en lugar de los extremos. Encuentro que la entropía tiende a ser mayor al principio y al final). Y las cadenas más cortas que eso rellenan los caracteres restantes con
\0
.Ahora, puedes deshacerte del
map
todo y usar una tabla hash. Si realmente tiene más de 100,000 tipos diferentes de tipos de eventos, esta podría ser una buena idea. Pero no sé de un juego en el que eso sería algo remotamente razonable.fuente
Para responder a la pregunta general:
No hay ninguno. Todo lo que puede hacer es identificar las necesidades específicas que tendrá para sus sistemas de eventos (puede haber varias diferentes) y luego usar la herramienta adecuada para el trabajo correcto.
He implementado e intentado generalizar toneladas de sistemas de eventos y he llegado a esta conclusión. Debido a que los intercambios son realmente diferentes si lo hace en tiempo de compilación o en tiempo de ejecución, si usa jerarquías de objetos o pizarras, etc.
La mejor manera es estudiar los diferentes tipos de sistemas de eventos y luego tendrás pistas sobre cuál usar en cada caso.
Ahora, si desea el sistema realmente más flexible, implemente un sistema de pizarra (tiempo de ejecución) con propiedades dinámicas de eventos y tendrá algo muy flexible pero quizás muy lento.
fuente