Las Cases Base: TSingleton, RTTI, DataPack
El motor se dividirá en parias partes según el valor estructural que tengan. En el namespace Base, tendremos las clases, macros y funciones útiles para construir la estructura superior. En nuestro caso tendremos tres clases por el momento.
TSingleton es un template que sirve para hacer singletons (clases con una única instancia, accesible desde cualquier punto del código). En los comentarios del archivo de cabecera se da un ejemplo de uso.
RTTI es un sistema ligero de identificación de tipo de objeto en tiempo de ejecución (Run Time Type Information), esta es una característica de C++ que permite consultar el nombre de la clase de una instancia y si es derivada de esta. En nuestro caso no viene bien tener un sistema de estos, pues podremos consultar que tipo de objeto es cada objeto del juego.
DataPack tiene la función de almacenar datos para transmitirlos. El objetivo de esta clase es doble. Por una parte quiero evitar tener que enviar puntero a estructura cuando la ocasión requiera datos extras, y por otro esta diseñado para poder ser enviado a través de la red a otro usuario en futuros juegos en red. Tiene un tamaño máximo de 1400 bytes que es el tamaño mínimo garantizado para que cualquier router que se encuentre no trocee el mensaje.
MessageHandler: El padre de todos los componentes
En nuestro futuros juegos los distintos elementos se van a componer de piezas que se van a comunicar entre si por mensajes. Para que esto funciona cada pieza debe tener un identificador único. Para tener esto de forma centralizada he creado una clase base, de la que todas las piezas heredaran.
MessageHandler es una clase base no instanciable, pues tiene una función virtual pura. Esta función es HandleMessage que se encargara de procesar los mensajes en las clases derivadas.
Tiene otra clase virtual, Notify, que se tiene que sobre escribir si se quieren recibir alguna notificación desde el sistema correspondiente.
Se puede destacar que tanto HandlMessage como Notify utilizan un DataPack como parámetro para proporcionar datos extras a estas funciones. Originalmente había pensado en poner un void* que luego se convertiría a los datos adecuados, pero los punteros tienen mucho peligro y la gestión de memoria es un infierno en sistemas complejos.
Otra de las tareas de esta clase es asignar de forma automática un identificador a cada nueva instancia de este tipo que se cree. El identificador no es mas que un entero sin signo que va incrementándose con cada nueva instancia.
Cuando me ponga a hacer el sistema online este identificador cambiara, pues habrá que tener en cuenta que un mismo id de distintos usuarios tienen que convivir en todos los clientes. Ya veremos como lo hago.
MessageDispatcher: El corazón del sistema
El núcleo de todo nuestro motor es un sistema de mensajería. Este esta implementado como MessageDispatcher, un singleton que registra los componentes que se crean y permite enviar mensajes entre los distintos componentes.
Básicamente es un mapa de punteros a MessageHandler ordenados por su identificador, y además almacena los mensajes que hay que enviar con algún retraso. Por ejemplo, una fabrica de tanques de RTS puede enviarse a si mismo un mensaje de “acabar nueva unidad en 20 segundos” y cuando se procese el mensaje generar la nueva unidad en el juego.
El constructor de MessageHandler se auto registra, y el destructor se auto desregistra por lo que con simplemente crear un objeto que derive de MessageHandler ya lo tenemos integrado en el sistema. Esto viene muy bien, pues nuestros elementos de juego se crearan a través de un patrón de diseño llamado factoría, que es un objeto especializado en crear otros objetos.
Notifications: Cuando las cosas ocurren
Otro elemento importante son el sistema de notificaciones. Este esta basado en un patrón llamado observador. En nuestro caso Notifications mantiene una lista de punteros MessageHandler que se registran para “observar” si ocurre un evento, un evento no es mas que un identificador lanzado en el momento adecuado por alguna parte del sistema.
Cuando el evento ocurre, todos los MessageHandler que estaban “observando”, es decir se habían registrado en Notifications, reciben a través de la función Notify la señal de que la notificación a ocurrido.
Las notificaciones pueden tener o no datos extra por eso esta sobrecargado. Hay una versión que solo recibe la notificación y hay otro que además tiene un DataPack para añadir datos.
Un ejemplo de notificación con datos seria, “granada” con datos extra “x:1027 y:2089” con lo que todos las unidades que escuchen ese mensaje saben que hay una granada en ese punto y pueden huir del lugar.
Conclusión
Este es solo el principio de la aventura de crear un motor de juegos. La librería esta en la parte de InternalLib, por lo que podéis descargarla e inspeccionar un poco el código. Todos los archivos de cabecera están documentados.
Gracias por vuestro tiempo. Un saludo.
No hay comentarios:
Publicar un comentario