Nessi Engine, primera parte

Tuesday, June 20th, 2006 @ 20:20 | Nessi, Proyectos

Hará alguna semana, retomé el desarrollo de GeekWars, un juego que tenía en mente y que como de costumbre por la falta de tiempo había dejado aparcado. La Campus Party se acerca y es deber moral presentar algo, por poco que sea. Así pues.. manos a la obra!

Revisando la versión iniciada tiempo atrás, poco me servía. No llevaba mucho, apenas la estructura principal y varias funciones… que aunque funcionaba, no terminaban de convencerme. Por tanto, comencé de nuevo a estructurar todo… con una idea ya más clara de lo que quería. Hablando ya en presente, tras adaptar la función main a las nuevas necesidades y punto de vista, toca pensar en cómo se gestionará todo el cotarro…

Nessi Engine, pretende ser un mini-engine que controle todos los aspectos del juego de una manera más o menos independiente. Con el tiempo, puede que hasta sea un engine completo, pero debido a que los días se echan encima, habrá que conformarse y hacerlo un poco menos genérico. Nessi, llamado así en honor a una colega campusera, está compuesto de varios archivos, principalmente engine.h y engine.cpp. En ellos estará la definición y código de todas las clases, estructuras de datos, funciones…. que el juego necesite. Por otro lado, a fin de tener todo bien organizado, el programa hace uso de dos librerías más, dependientes de Nessi, una de efectos gráficos, gfx; y otra de funciones generales, principalmente de seteo, carga y utilidades varias.

Nessi cuenta ya con clases y funciones vitales, que se irán mejorando y ampliando según vaya progresando el desarrollo. Algunas de ellas son MSprite, MTile, MFrame y Nessi_Log. La primera almacena sprites, la segunda tiles y la tercera, frames de los sprites y tiles. La última, es la que gestiona el sistema de log, pasando a un fichero de texto todo lo que ocurre en el programa.

 

MSprite, MTile, MFrame. La idea de estas tres clases surgió de un buen libro de nivel iniciación / medio famosete por el mundo de SDL, más ahora que lo han liberalizado y se encuentra disponible en la red, en formato pdf. (Programación de VideoJuegos con SDL, por Alberto García Serrano). Hace tiempo que lo tengo en papel, me costó encontrarlo, pero mereció la pena. En algunos puntos no estoy muy de acuerdo con la forma de implementación, pero te da una idea clara de por donde caminar a lo largo del desarrollo de un juego. Haciendo otro pequeño offtopic (risas), recomiendo visitar el blog de Benko (ver enlaces), que se curró unos tutoriales de iniciación a SDL muy interesantes. Venga, que ya retomo el tema de las tres clases (más risas). MFrame almacena el frame o gráfico que le pasa como parámetro en su función miembro void Load(char *path);, guardando la ruta en una cadena de caracteres llamada name.

 

/* Clase donde se almacenarán los frames de cada sprite o tile */
class MFrame {

public:

char name[30];
SDL_Surface *frame;
void Load(char *path);
void Unload();

};

En breve name pasará a ser privado, creando la función que lo devuelva. (Actualmente esta así para simplicar pruebecillas varias).

 

MSprite es ya más grande y compleja. Por un lado, tiene la capacidad de crear un array dinámico de MFrame, donde estarán todos los frames del sprite. Al crear un objeto MSprite, se le pasa el número de frames que tendrá y el constructor de la clase se encarga de generar ese array, vacío, que luego se irá completando con una función miembro llamada AddFrame y que controlará el número máximo de frames a meter y el número de frames metidos o cargados. La clase también sabe en todo momento que frame dibujar, gracias a una variable que guarda ese dato y que va cambiando según la necesidad. Además, tiene funcionalidades relativas al posicionamiento y movimiento del sprite en pantalla. MSprite tiene una clase hija, que hereda sus características y añade dos más, SpaceShip. Aparte de todo lo anterior, SpaceShip controla el arma disponible en cada momento y la salud de la nave. De momento para no complicarlo, sólo tendrá un tipo de disparo a la vez, pero por supuesto se implementará que recoja tipos de disparo por el camino y éstos se vayan almacenando, pudiendo elegir arma entre las disponibles.

 

MTile, encargada de almacenar los mapas de cada fase, también se compone de un array de clases MFrame. El tema de su carga, está siendo un tema costoso, si bien parece que hemos dado con una solución más o menos eficiente. Lo suyo sería haberse creado un editor de mapas, para lo que no hay tiempo, o en su defecto usar uno… pero tras probar varios.. ninguno me convenció (bien por el sistema, bien por no soportar determinadas extensiones gráficas….). Total, que dándole vueltas la cosa terminó por tener en una carpeta de niveles archivos de texto, uno por nivel. En ellos, se almacena la info sobre los frames que componen el mapa. El formato, números enteros (identificadores del frame a cargar) separados por un espacio, en 20 columnas (número de frames que caben en el width de la pantalla, siendo cada frame de 40×40 pixels) y N filas, dependiendo de la longitud del mapa. (En cada pantalla caben 15 frames en vertical). Que el fichero sea de texto, me viene muy bien, pues puedo cambiar de forma fácil el frame a mostrar en cada posición, pero tiene la desventaja de que todo el mundo puede tocarlo con facilidad. (Al terminar las pruebas, miraré a codificar la info de alguna manera, je,je,je). Por otro lado, existe un objeto MTile llamado mapResources, donde almaceno todos los frames disponibles para confeccionar el mapa de cada fase. Para la carga de un nivel, se llama a void SetLevel(MTile &st, MTile maps, char *lvl);, pasandola el objeto MTile donde guardar la info, el objeto mapResources y el nivel a cargar. Este último, lo paso como char, pues la función se va automáticamente al fichero adecuado según ese número pasado, componiendo la ruta en donde está, y leyendo todo el fichero progresivamente. Esa es la idea, pero tengo problemillas varias al hacer las conversiones de lo leido (char o string) a lo necesitado (un int para el índice del array de recursos). Habrá que seguir dándole vueltecillas.. :roll:

 

Nessi_Log. Con el fin de tener una idea de lo que pasa en cada momento, Nessi tiene un sistema propio de log, gestionado a través de esta clase. Nessi_Log tiene 3 funciones miembro, una para crear e inicializar el archivo de log, otra para escribir en él y otra para cerrarlo. Para escribir en el log, se llama a void Write(char *message, int br_up);, donde se le pasa el mensaje a escribir y el número de saltos de línea a escribir antes del mensaje.

Aparte de estas clases, hay varias funciones interesantes, como void DrawCountDown(MSprite &dcd, SDL_Surface *srf);, que dibuja la cuenta atrás de 3,2,1, go! antes del inicio de cada fase; o las funciones de carga como void SetMapResources(MTile &smr); o void SetCountDown(MSprite &cd);.

Superadas ya las 1000 líneas y con mucho curro por delante en 24 “dias” de desarrollo, seguiremos informando…

Leave a Reply