Intro a la programación de juegos

Por Nacho Cabanes

Contenido Indice Cambios Enlaces Autor

9. Evitemos los parpadeos. Cuarto juego (aproximación "c"): Miniserpiente 3.

Contenido de este apartado:


9.1. Idea básica: el doble buffer.

Nuestros juegos, a pesar de que aún son sencillos, empiezan a tener problemas de parpadeos. Podríamos sincronizar el refresco de la pantalla con el momento de dibujar nuestras figuras, y el problema se reduciría un poco, pero seguiría existiendo. Y es tanto más grave cuantos más elementos dibujamos en pantalla (especialmente en el caso de Pascal, en que hemos usado una rutina "nuestra" para dibujar los sprites, en vez de rutinas "prefabricadas").

Por eso, en la práctica se suele hacer una aproximación ligeramente distinta a la que hemos usado hasta ahora: no escribiremos directamente en pantalla, sino que prepararemos todo lo que va a aparecer, lo dibujaremos en una "pantalla no visible" y en el último momento volcaremos toda esta "pantalla no visible" a la vez. Esta técnica es lo que se conoce como el empleo de un "doble buffer" (en inglés "double buffering").

En informática, un "buffer" es una memoria intermedia en la que se guarda la información antes de llegar a su destino definitivo. Un ejemplo típico es el "buffer" de impresora.

En nuestro caso, esto nos permite preparar toda la información tranquilamente, en un sitio que todavía no será su destino definitivo. Cuando todo está listo, es el momento en el que se vuelca a pantalla, todo a la vez, minimizando los parpadeos.

La forma de conseguir esto con Allegro es algo parecido a:

   BITMAP *bmp = create_bitmap(320, 200);    // Creamos el bitmap auxiliar en memoria
   clear_bitmap(bmp);                        // Lo borramos
   putpixel(bmp, x, y, color);               // Dibujamos en él
   blit(bmp, screen, 0, 0, 0, 0, 320, 200);  // Finalmente,volcamos a pantalla
 

Los cambios en nuestro programa no son grandes: creamos un nuevo bitmap llamado "pantallaOculta", dibujamos en él la nave,los marcianos y el disparo, volcamos a pantalla al final de cada secuencia completa de dibujado, y esta vez no nos molestamos en esperar a que realmente haya "información nueva" en pantalla: volvemos a dibujar aunque realmente sea lo mismo que ya había antes. En cualquier caso, ahora no debería haber parpadeos o estos deberian ser mínimos.


9.2 Miniserpiente 3 en C. 

Pocos cambios...
 
/*----------------------------*/
/*  Intro a la programac de   */
/*  juegos, por Nacho Cabanes */
/*                            */
/*    IPJ09C.C                */
/*                            */
/*  Noveno ejemplo: juego de  */
/*  "miniSerpiente" (aprox C) */
/*                            */
/*  Comprobado con:           */
/*  - MinGW DevStudio 2.05    */
/*    (gcc 3.4.2) y Allegro   */
/*    4.03, Windows XP        */
/*----------------------------*/

#include <allegro.h>


/* Posiciones X e Y iniciales */ #define POS_X_INI 16 #define POS_Y_INI 10
#define INC_X_INI 1 #define INC_Y_INI 0
/* Pausa en milisegundos entre un "fotograma" y otro */ #define PAUSA 350
/* Teclas predefinidas */ #define TEC_ARRIBA KEY_E
#define TEC_ABAJO KEY_X
#define TEC_IZQDA KEY_S
#define TEC_DCHA KEY_D

int posX, posY; /* Posicion actual */ int incX, incY; /* Incremento de la posicion */
/* Terminado: Si ha chocado o comida todas las frutas */ int terminado;
/* La tecla pulsada */ int tecla;
/* Escala: relacion entre tamaño de mapa y de pantalla */ #define ESCALA 10
/* Ancho y alto de los sprites */ #define ANCHOSPRITE 10 #define ALTOSPRITE 10
/* Y el mapa que representa a la pantalla */ /* Como usaremos modo grafico de 320x200 puntos */ /* y una escala de 10, el tablero medira 32x20 */ #define MAXFILAS 20 #define MAXCOLS 33
char mapa[MAXFILAS][MAXCOLS]={ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "X X X", "X F X X", "X F X F X", "X XXXXX X X", "X X X X", "X X X X X", "X X X X XXXX", "X X X X", "X X X X", "X X X X", "X F X X", "X X X", "X X F X", "X X X X", "X X X X", "X X F X X", "X F X X X", "X X F X", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" };
int numFrutas = 8;
/* Nuestros sprites */ BITMAP *ladrilloFondo, *comida, *jugador; BITMAP *pantallaOculta; /* Y la pantalla oculta */

typedef char tipoSprite[ANCHOSPRITE][ALTOSPRITE]; /* El sprite en si: matriz de 10x10 bytes */
tipoSprite spriteLadrillo = {{0,2,2,2,2,2,2,2,2,0}, {2,1,1,1,1,1,1,1,1,2}, {2,1,1,1,1,1,1,1,1,2}, {2,1,1,1,1,1,1,1,1,2}, {2,1,1,1,1,1,1,1,1,2}, {2,1,1,1,1,1,1,1,3,2}, {2,1,1,1,1,1,1,3,3,2}, {2,1,1,1,1,1,3,3,2,2}, {2,2,2,2,2,2,2,2,2,0} };
tipoSprite spriteComida = {{0,0,0,2,0,0,0,0,0,0}, {0,0,2,2,0,0,2,2,0,0}, {0,4,4,4,2,2,4,4,0,0}, {4,4,4,4,4,2,4,4,4,0}, {4,4,4,4,4,4,4,4,4,0}, {4,4,4,4,4,4,4,4,4,0}, {4,4,4,4,4,4,4,4,4,0}, {4,4,4,4,4,4,4,4,4,0}, {0,4,4,4,4,4,4,4,0,0} };
tipoSprite spriteJugador = {{0,0,3,3,3,3,3,0,0,0}, {0,3,1,1,1,1,1,3,0,0}, {3,1,1,1,1,1,1,1,3,0}, {3,1,1,1,1,1,1,1,3,0}, {3,1,1,1,1,1,1,1,3,0}, {3,1,1,1,1,1,1,1,3,0}, {0,3,1,1,1,1,1,3,0,0}, {0,0,3,3,3,3,3,0,0,0} };
/* -------------- Rutina de crear los sprites ------------- */

void creaSprites() { int i, j;
ladrilloFondo = create_bitmap(10, 10); clear_bitmap(ladrilloFondo); for(i=0; i<ANCHOSPRITE; i++) for (j=0; j<ALTOSPRITE; j++) putpixel(ladrilloFondo, i, j, palette_color[ spriteLadrillo[j][i] ]);
comida = create_bitmap(10, 10); clear_bitmap(comida); for(i=0; i<ANCHOSPRITE; i++) for (j=0; j<ALTOSPRITE; j++) putpixel(comida, i, j, palette_color[ spriteComida[j][i] ]);
jugador = create_bitmap(10, 10); clear_bitmap(jugador); for(i=0; i<ANCHOSPRITE; i++) for (j=0; j<ALTOSPRITE; j++) putpixel(jugador, i, j, palette_color[ spriteJugador[j][i] ]);
pantallaOculta = create_bitmap(320, 200); }

/* -------------- Rutina de dibujar el fondo ------------- */
void dibujaFondo() { int i, j;
clear_bitmap(pantallaOculta);
for(i=0; i<MAXCOLS; i++) for (j=0; j<MAXFILAS; j++) { if (mapa[j][i] == 'X') draw_sprite(pantallaOculta, ladrilloFondo, i*ESCALA, j*ESCALA); if (mapa[j][i] == 'F') draw_sprite(pantallaOculta, comida, i*ESCALA, j*ESCALA); }
}

/* ------------------------------------------------ */ /* */ /* -------------- Cuerpo del programa ------------- */
int main() {
allegro_init(); /* Inicializamos Allegro */ install_keyboard(); install_timer();
/* Intentamos entrar a modo grafico */ if (set_gfx_mode(GFX_SAFE, 320, 200, 0, 0) != 0) { set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); allegro_message( "Incapaz de entrar a modo grafico\n%s\n", allegro_error); return 1; }
/* ----------------------- Si todo ha ido bien: empezamos */
creaSprites(); dibujaFondo();
/* Valores iniciales */ posX = POS_X_INI; posY = POS_Y_INI;
incX = INC_X_INI; incY = INC_Y_INI;

/* Parte repetitiva: */ do { dibujaFondo(); draw_sprite (pantallaOculta, jugador, posX*ESCALA, posY*ESCALA);
terminado = FALSE;
/* Si paso por una fruta: la borro y falta una menos */ if (mapa[posY][posX] == 'F') { mapa[posY][posX] = ' '; numFrutas --; if (numFrutas == 0) { textout(pantallaOculta, font, "Ganaste!", 100, 90, palette_color[14]); terminado = TRUE; } }

/* Si choco con la pared, se acabo */ if (mapa[posY][posX] == 'X') { textout(pantallaOculta, font, "Chocaste!", 100, 90, palette_color[13]); terminado = TRUE; }
/* En cualquier caso, vuelco la pantalla oculta */ blit(pantallaOculta, screen, 0, 0, 0, 0, 320, 200);
if (terminado) break;
/* Compruebo si se ha pulsado alguna tecla */ if ( keypressed() ) { tecla = readkey() >> 8;
switch (tecla) { case TEC_ARRIBA: incX = 0; incY = -1; break; case TEC_ABAJO: incX = 0; incY = 1; break; case TEC_IZQDA: incX = -1; incY = 0; break; case TEC_DCHA: incX = 1; incY = 0; break; }
}
posX += incX; posY += incY;
/* Pequeña pausa antes de seguir */ rest ( PAUSA );
} while (TRUE); /* Repetimos indefininamente */ /* (la condición de salida la comprobamos "dentro") */
readkey(); destroy_bitmap(pantallaOculta); destroy_bitmap(jugador); destroy_bitmap(comida); destroy_bitmap(ladrilloFondo); return 0;
}
/* Termino con la "macro" que me pide Allegro */ END_OF_MAIN();





8.3. Miniserpiente 3 en Pascal.

(Todavía no disponible: la biblioteca gráfica estándar de Free Pascal no permite usar un doble buffer)





9.4. Miniserpiente 3 en Java.

(Todavía no disponible; pronto lo estará)


Contenido Indice Cambios Enlaces Autor
 
Nacho Cabanes, 2005
Última versión en www.pobox.com/users/ncabanes