05 octubre 2008

Remake (parcial) de Fruity Frank... 01 - Mostrar el fondo

En este primer acercamiento vamos a preparar las herramientas que usaremos para crear el juego, y las probaremos para mostrar la imagen de fondo del juego.


  • Como entorno de desarrollo usaremos CodeBlocks, que incluye el compilador GNU de C y C++, junto con entorno integrado (editor y depurador). Se podría usar cualquier otro, pero en el apartado de "Descargas" dejaré un proyecto de CodeBlocks listo para ser usado simplemente con un "doble clic" desde Windows. Quien elija usar otro entorno de desarrollo tendrá que hacer un poco más de "trabajo manual".

  • Como biblioteca que nos proporcione las funcionalidades básicas usaremos SDL (Simple DirectMedia Layer)

  • Nos apoyaremos en un par de ficheros auxiliares que se encargan de ocultar los detalles de SDL y de permitirnos usar órdenes traducidas al español, algo más sencillas.  Todas estas órdenes comenzarán por "SDLA_", para distinguirlas de las órdenes "normales" de C++ y/o de SDL.



Usando estos ficheros auxiliares, un primer fuente de prueba sería así:

#include "SDL_ASI.h"

int main (int argc, char** argv)
{
SDLA_inicializar(640,480,24);

SDLA_Imagen* fondoPantalla;
fondoPantalla=SDLA_cargarImagen("fondo.bmp");

SDLA_dibujarImagenOculta(fondoPantalla,0,0);
SDLA_visualizarOculta();

SDLA_esperarTecla();
return 0;
}


Vamos a ver línea por línea lo que hace, porque entenderlo será vital para poder irlo ampliando...

  • #include "SDL_ASI.h" - Carga uno de los ficheros auxiliares que necesitamos, el fichero de cabecera "SDL_ASI.h".

  • int main (int argc, char** argv) - Comienzo del cuerpo del programa ("main").

  • SDLA_inicializar(640,480,24);
    - Inicializa la pantalla gráfica, escogiendo el modo de 640 x 480 puntos (ancho x alto) y 24 bits de color (16 millones de colores).

  • SDLA_Imagen* fondoPantalla; - Declara una variable llamada "fondoPantalla", que será una imagen (tipo SDLA_Imagen*)

  • fondoPantalla=SDLA_cargarImagen("fondo.bmp");
    - Prepara el valor de "fondoPantalla": será una imagen que se cargue de disco, llamada "fondo.bmp".

  • SDLA_dibujarImagenOculta(fondoPantalla,0,0); - Dibuja la imagen "fondoPantalla" en las coordenadas (0,0) de la pantalla oculta, es decir en la esquina superior izquierda. Para evitar parpadeos en pantalla, siempre dibujaremos los elementos de nuestro juego uno a uno en una pantalla oculta, y finalmente haremos que toda esa pantalla oculta pase a ser visible.

  • SDLA_visualizarOculta();
    - Hace que la pantalla oculta se pueda ver.

  • SDLA_esperarTecla(); - Esperamos a que se pulse una tecla.

  • return 0; - Termina el cuerpo del programa.



Para probar todo esto, bastaría con descargar el entorno CodeBlocks, añadirle los paquetes de SDL (al menos SDL y SDL_ttf) desde el menú hacer doble clic en el fichero del proyecto: "fruity.cbp". Sólo hace falta que hayamos preparado una imagen de fondo del juego, que se llame "fondo.bmp", que podríamos haber capturado del juego original, usando un emulador (esta imagen forma parte del proyecto que se puede descargar).

Para poner en marcha el "supuesto juego" (que todavía es poco jugable), entraríamos al menú "Build" (construir) de CodeBlocks y escogeríamos la opción "Build & Run" (construir y ejecutar). Si todo es correcto (debería ser así), debería aparecer la pantalla de fondo del juego y esperar que pulsemos una tecla, momento en el que se acaba "el juego".




Para los que tengan más conocimientos técnicos, voy a dar algún detalle más de cómo son los otros dos ficheros auxiliares que forman parte de nuestro proyecto.

El primero de ellos, el que incluimos desde nuestro juego, se llama SDL_ASI.h, y es un fichero de cabecera que tiene los resúmenes de las posibilidades de nuestra "mini-biblioteca" SDL_ASI, junto con las declaraciones de constantes:

#ifndef SDL_ASI_H

#define SDL_ASI_H

#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>

/* Adaptacion de SDL, ocultando lo basico y
traduciendo al castellano */

/* =====================================
Versiones anteriores:

Numero Fecha Cambios
------- ---------- ----------------

0.04 18/05/2007 Permite escribir texto en la pantalla oculta
(necesita SDL_TTF)
Añadida una funcion para vaciar buffer de
teclado

0.03 18/05/2007 Añadido "esperaTecla", para poder esperar a
que se pulse una tecla sin necesidad de "while".
Añadidas varias constantes más para teclas habituales:
números del 0 al 5, espacio.

0.02 27/04/2007 Añadido "colision", corregido "teclaPulsada",
que usaba parametro "char" en vez de "int",
separados prototipos (.h) de codigo (.c)

0.01 26/04/2007 Versión inicial, que enmascara SDL: permite
inicializar, borrar la pantalla oculta,
cargar una imagen, dibujar una imagen en la
pantalla oculta, visualizar la pantalla
oculta, ver si se ha pulsado el ratón, ver
si se ha pulsado una tecla, y define las teclas
del cursor y ESC.


===================================== */

#define SDLA_Imagen SDL_Surface
#define SDLA_Fuente TTF_Font
#define SDLA_Color SDL_Color

#define TECLA_ESC SDLK_ESCAPE
#define TECLA_DER SDLK_RIGHT
#define TECLA_ARR SDLK_UP
#define TECLA_ABA SDLK_DOWN
#define TECLA_IZQ SDLK_LEFT

#define TECLA_J SDLK_j
#define TECLA_A SDLK_a
#define TECLA_C SDLK_c
#define TECLA_R SDLK_r
#define TECLA_V SDLK_v
#define TECLA_1 SDLK_1
#define TECLA_2 SDLK_2
#define TECLA_3 SDLK_3
#define TECLA_4 SDLK_4

#define TECLA_ESP SDLK_SPACE

/*---------------------------*/


int SDLA_inicializar(int ancho, int alto, int colores);

SDLA_Imagen* SDLA_cargarImagen(char *fichero);

void SDLA_colorTransparente(SDLA_Imagen* imagen,
int r, int g, int b);

int SDLA_borrarPantallaOculta(int r, int g, int b);

int SDLA_dibujarImagenOculta(SDLA_Imagen *imagen,
int x,int y);

int SDLA_escribirTextoOculta(char *frase,
int x,int y, int color, SDLA_Fuente *fuente);

int SDLA_visualizarOculta();

int SDLA_teclaPulsada(int c);

int SDLA_esperarTecla();

void SDLA_vaciarBufferTeclado();

int SDLA_ratonPulsado(int *x, int *y);

int SDLA_pausa(long milisegundos);

int SDLA_colision(int x1, int y1, int an1, int al1,
int x2, int y2, int an2, int al2);

#endif



El segundo fichero, que no incluimos directamente desde "juego.cpp", pero es parte del proyecto, se llama "sdlasi.c", y contiene los detalles de esas funciones:

#include "SDL_ASI.h"


/* Adaptacion de SDL, ocultando lo basico y
traduciendo al castellano */

SDLA_Fuente *SDLA_arial12;
SDLA_Fuente *SDLA_arial24;

SDLA_Imagen* pantallaOculta;
SDLA_Imagen* pantallaVisible;


int SDLA_inicializar(int ancho, int alto, int colores) {
// Inicializo SDL
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
exit(1);

// Me aseguro de que SDL limpia al salir
atexit(SDL_Quit);

// Creo la pantalla visibl
pantallaVisible = SDL_SetVideoMode(
ancho, alto, colores,
SDL_HWSURFACE|SDL_DOUBLEBUF);
if ( !pantallaVisible )
exit(2);

// Y la oculta
pantallaOculta = SDL_CreateRGBSurface(SDL_SWSURFACE,
ancho,alto,colores, 0,0,0,0);
if ( !pantallaOculta )
exit(3);

// Inicializo SDL_TTF
if ( TTF_Init() < 0 )
exit(5);
atexit(TTF_Quit);

SDLA_arial12 = TTF_OpenFont("arial.ttf",12);
SDLA_arial24 = TTF_OpenFont("arial.ttf",24);

return 0;
}

SDLA_Imagen* SDLA_cargarImagen(char *fichero) {
SDLA_Imagen* imagen;
imagen = SDL_LoadBMP(fichero);
if (imagen == NULL)
exit(4);
return imagen;
}


void SDLA_colorTransparente(SDLA_Imagen* imagen,
int r, int g, int b){

SDL_SetColorKey(imagen,
SDL_SRCCOLORKEY|SDL_RLEACCEL,
SDL_MapRGB(imagen->format, r,g,b));
}

int SDLA_borrarPantallaOculta(int r, int g, int b) {
SDL_FillRect(pantallaOculta,
0,SDL_MapRGB(pantallaOculta->format,
r,g,b));
}

int SDLA_dibujarImagenOculta(SDLA_Imagen *imagen,
int x,int y){

SDL_Rect posicion;
posicion.x = x;
posicion.y = y;
SDL_BlitSurface(imagen, 0,
pantallaOculta, &posicion);
}

int SDLA_escribirTextoOculta(char *frase,
int x,int y, int color, SDLA_Fuente *fuente){

SDLA_Color bgcolor,fgcolor;
SDLA_Imagen *textoComoImagen;

/* El color de primer plano sera el escogido */
fgcolor.r = (color & 0xff0000)>>16;
fgcolor.g = (color & 0x00ff00)>>8;
fgcolor.b = color & 0x0000ff;

/* El fondo sera negro (transparente) */
bgcolor.r=0;
bgcolor.g=0;
bgcolor.b=0;

/* Preparo el bitmap */
textoComoImagen = TTF_RenderText_Shaded(fuente,
frase,fgcolor,bgcolor);
SDLA_colorTransparente(textoComoImagen,0,0,0);

/* Y dibujo */
SDLA_dibujarImagenOculta(textoComoImagen, x,y);
}

int SDLA_visualizarOculta() {
SDL_BlitSurface(pantallaOculta, 0,
pantallaVisible, 0);
SDL_Flip( pantallaVisible );
}

int SDLA_teclaPulsada(int c) {
SDL_PumpEvents();
Uint8 *keys = SDL_GetKeyState(NULL);
if (keys[c] == 1)
return 1;
else
return 0;
}

int SDLA_esperarTecla() {
SDL_Event suceso;

/* Imitacion de "getch()" para SDL */
while (1) { /* Repetimos indefinidamente */
while (SDL_PollEvent(&suceso)) { /* Comprobamos sucesos */
if (suceso.type == SDL_KEYDOWN) /* Si es tecla pulsada */
return suceso.key.keysym.sym; /* La devolvermos */
}
}

}

void SDLA_vaciarBufferTeclado() {
SDL_Event suceso;

while ((SDL_PollEvent(&suceso)) &&
(suceso.type == SDL_KEYDOWN))
;
}


int SDLA_ratonPulsado(int *x, int *y) {
SDL_PumpEvents();

return(SDL_GetMouseState(x,y)&SDL_BUTTON(1));
}

int SDLA_pausa(long milisegundos) {
SDL_Delay(milisegundos);
}

int SDLA_colision(int x1, int y1, int an1, int al1,
int x2, int y2, int an2, int al2) {
if ((x2+an2 > x1)
&& (y2+al2 > y1)
&& (x1+an1 > x2)
&& (y1+al1 > y2))
return 1;
else
return 0;
}


Siendo estrictos, necesitamos dos cosas más (que también se pueden descargar desde la página del proyecto): las DLL llamadas SDL.DLL y SDL_TTF.DLL, que deberían estar accesibles (al menos en el PATH, pero para distribuir nuestro juego puede ser cómodo que se encuentren en la misma carpeta que el ejecutable) y el tipo de letra ARIAL.TTF (en las msimas condiciones).

¿Y cual es la página del proyecto? code.google.com/p/fruityfrank