Clase Juego: la aplicación en sí

Buenas a todos una vez más, hoy me he animado a pegarle un buen empujón al proyecto, y aunque los resultados son de esos que no se pueden mostrar (al menos de momento, porque están sobre papel), voy a aprovechar para escribir el último artículo descriptivo sobre las clases que componen LibWiiEsp.

Comentar que todo lo publicado en el blog estará recogido en la carpeta doc de la propia biblioteca, bien en el manual (que empezaré a redactar el lunes), o en la referencia completa que se puede generar de forma automática gracias a Doxygen. Ahora mismo, teniendo una versión de trabajo actualizada de la Forja, se puede entrar en /trunk/libwiiesp y ejecutar la instrucción make doc, de tal manera que se genera esta documentación (y cuando esté el manual de instalación y uso de la biblioteca, se generará desde esa misma orden también).

Para hacer un resumen de lo publicado hasta ahora, tenemos las instrucciones para instalar el entorno de desarrollo para Nintendo Wii utilizando LibWiiEsp, una explicación de cómo funciona el Makefile (entrada ésta que tengo que actualizar), y una descripción detallada de cada una de las clases y subsistemas que componen la biblioteca. Centrándome un poco mas en la entrada de hoy, con la descripción de la clase Juego finalizo este apartado, y daré por concluida la referencia completa de la biblioteca. Como ya comenté, lo siguiente será el manual, que espero tenerlo listo durante la semana santa. Por lo demás, cuando el manual esté acabado, publicaré la versión 1.0 de la biblioteca (ahí es ).

En fin, al lío. Os presento la clase Juego:

Esta clase abstracta proporciona una plantilla sobre la que construir el objeto principal de toda aplicación desarrollada con LibWiiEsp. Consiste en dos apartados bien diferenciados, que son la inicialización de todos los subsistemas de la consola y de la biblioteca (esto se realiza desde el constructor), y la ejecución del bucle principal de la aplicación. Como clase abstracta que es, no se puede instanciar directamente, si no que hay que crear a partir de ella una clase derivada en la que se definan los métodos que el programador considere oportunos para gestionar el programa.

Además de la inicialización de la consola y el control del bucle principal del programa, esta clase también se encarga de gestionar la entrada de hasta cuatro mandos en la consola. Para ello, dispone de un diccionario (de tipo std::map) en el que cada jugador, que está identificado por un código único (de tipo std::string), tiene asociado un mando concreto de la consola. Para acceder al mando de un jugador, basta con buscar en el diccionario mediante el código identificativo del éste.

El constructor de la clase recibe como parámetro un archivo XML con un formato concreto, en el que se deben especificar las opciones de configuración para el programa, como son el nivel de logging deseado y la ruta completa del archivo de log que se generará en el caso de estar el sistema de log activado, el color considerado transparente y que no se dibujará en la pantalla (en formato 0xRRGGBBAA, ver documentación de la clase Screen para más información), el número de fotogramas por segundo (FPS) que se quiere que tenga el videojuego, la ruta absoluta hasta el archivo XML donde se especifican los recursos multimedia que deben ser cargados en la galería de medias (más información en la descripción de la clase Galeria), la ruta absoluta hasta el archivo XML donde se especifican las etiquetas de texto del sistema de idiomas y el nombre del idioma por defecto (más detalles en la descripción de la clase Lang), y por último, la configuración de jugadores, consistente en cuatro atributos de tipo cadena de caracteres en los que se deben especificar los códigos identificadores para cada uno de los jugadores.

A continuación, se muestra un ejemplo del archivo de configuración esperado:

<?xml version="1.0" encoding="UTF-8"?>
<conf>
<log valor="/apps/wiipang/info.log" nivel="3" />
<alpha valor="0xFF00FFFF" />
<fps valor="25" />
<galeria valor="/apps/wiipang/xml/galeria.xml" />
<lang valor="/apps/wiipang/xml/lang.xml" defecto="русский" />
<jugadores pj1="pj1" pj2="pj2" pj3="" pj4="" />
</conf>

El constructor de la clase Juego, que debe ser llamado en el constructor de la correspondiente clase derivada, se encarga en primer lugar de montar la primera partición de la tarjeta SD (debe ser una partición con sistema de ficheros FAT), cargar el árbol XML del archivo de configuración en memoria mediante la clase Parser, e inicializar el sistema de logging según se haya especificado en el archivo de configuración. Si en alguna de estas operaciones se produjera un error, se saldría del programa mediante una llamada a la función exit().

A partir de ese momento, el constructor inicializará los sistemas de vídeo (clase Screen), controles (clase Mando), sonido (clase Sonido) y la biblioteca FreeType de gestión de fuentes (clase Fuente). A continuación, establece el color transparente del sistema y el número de FPS, después de lo cual, leerá la configuración de los jugadores, y creará una instancia de Mando para cada jugador que, en el archivo de configuración, no tenga una cadena vacía como identificador. Posteriormente, guarda cada mando asociado al código del jugador correspondiente en el diccionario de Controles.

Por último, se cargan todos los recursos media en la Galeria, y las etiquetas de texto para los idiomas del sistema en la clase Lang.

Después de haber inicializado la consola partiendo del archivo de configuración, el método virtual run() proporciona una implementación básica del bucle principal del programa, en la que se controlan las posibles excepciones que puedan lanzarse (las cuales captura y almacena su mensaje en el archivo de log), se mantiene constante la tasa de fotogramas por segundo, y se actualizan todos los mandos conectados a la consola y se gestiona correctamente la actualización de los gráficos a cada fotograma. Junto a este método run(), también se proporciona un método virtual puro, llamado cargar(), y que se ejecuta antes de entrar en el bucle principal. Este método permite realizar las operaciones que se estimen necesarias antes de comenzar la ejecución del bucle, en el caso de que fueran necesarias (en caso contrario, bastaría con definir el método como una función vacía a la hora de derivar la clase Juego).

Igualmente, al ser el método run() virtual, si el programador necesita otro tipo de gestión para el bucle principal de la aplicación, basta con que lo redefina en la clase derivada; a pesar de ello, tendrá disponible un sencillo ejemplo de control del bucle de la aplicación en la definición del método en la clase Juego.

Por último, especificar un detalle, y es que el destructor de la clase Juego se encarga de liberar la memoria ocupada por los objetos de la clase Mando, de tal manera que no hay que preocuparse por ello.

Y eso es todo, con esto finalizo la descripción de las clases que componen LibWiiEsp, información que se puede encontrar en todo momento en la referencia de la biblioteca generada con Doxygen, o en los comentarios de los archivos de cabeceras (recomiendo generar la referencia, es más cómodo).

El siguiente paso, como ya comenté antes, será el manual completo de LibWiiEsp, en el que recogeré todos los pormenores sobre instalación del entorno, detalles a tener en cuenta a la hora de programar en Wii, cómo se desarrolla utilizando la biblioteca, qué hay que tener en cuenta al derivar las clases Actor, Nivel y Juego, cómo ejecutamos un programa en la consola, cómo se debuggea, y un largo etcétera. Pienso hacer un manual absolutamente completo, a ver si sirve para despertar el interés de la comunidad, que de momento sigue siendo pequeña.

Más noticias pronto.

, , , , , ,

  1. #1 por Antonio García Domínguez el 17/abril/2011 - 09:00

    Es curioso que obligues a los usuarios a acordarse del detalle de exit() al salir de la aplicación. ¿No podrías hacer la llamada en el constructor? Si en el main() creas una instancia de Juego, se llamará automáticamente a su constructor al salir, ¿no?

    • #2 por rabbitbodom el 17/abril/2011 - 10:28

      Pues sí que es un poco engorroso, de hecho, antes lo tenía puesto como comentas (llamada a exit() en el destructor de Juego), y lo cambié porque no sabía hasta qué punto sería correcto. Pero ya que lo dices, es mucho más cómodo si el programador no tiene que tener en cuenta ese detalle. Gracias por la observación.

  2. #3 por Antonio García Domínguez el 17/abril/2011 - 10:35

    Bueno, me refería al destructor, no al constructor. Qué despiste :-D.

Deja un comentario