22. Un doble buffer para evitar parpadeos. Octavo juego (aproximación "e"): Marciano 6. (*)
Habíamos comentado en el tema 9 cómo podíamos hacer un "doble buffer", para dibujar en memoria, en vez de hacer directamente en pantalla, y volcar el resultado en el momento final, evitando parpadeos:
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
Nuestro "bitmap auxiliar" se lamará "pantallaOculta". Los cambios en el fuente son muy pocos:
/*------------------------------*/ /* Intro a la programac de */ /* juegos, por Nacho Cabanes */ /* */ /* ipj22c.c */ /* */ /* Ejemplo: */ /* Varios marcianos y una */ /* nave movindose de */ /* forma independiente; la */ /* nave puede disparar y */ /* "matar" a los marcianos */ /* Eliminados parpadeos */ /* */ /* Comprobado con: */ /* - Djgpp 2.03 (gcc 3.2) */ /* y Allegro 4.02 - MsDos */ /* - MinGW 2.0.0-3 (gcc 3.2) */ /* y Allegro 4.02 - WinXP */ /* - DevC++ 4.9.9.2(gcc 3.4.2) */ /* y Allegro 4.03 - WinXP */ /* - Gcc 3.2.2 + All. 4.03 */ /* en Mandrake Linux 9.1 */ /*------------------------------*/ #include <allegro.h> /* -------------- Constantes globales ------------- */ #define ANCHOPANTALLA 320 #define ALTOPANTALLA 200 // Los dos siguientes valores son los retardos // hasta mover la nave y el marciano, pero ambos // en centsimas de segundo #define RETARDOCN 8 #define RETARDOCM 30 // Y tambin el del disparo #define RETARDOCD 12 #define MARGENSUP 20 #define MARGENDCHO 25 #define MARGENIZQDO 25 #define MARGENINF 30 #define INCREMX 3 #define INCREMY 7 #define FILASMARCIANOS 5 #define COLUMNASMARCIANOS 11 #define DISTANCIAXMARCIANOS 20 #define DISTANCIAYMARCIANOS 12 // Datos de cada marciano typedef struct { int posX; int posY; int activo; } marciano; /* -------------- Variables globales -------------- */ PALETTE pal; BITMAP *imagen; BITMAP *nave; BITMAP *marciano1; BITMAP *marciano2; BITMAP *disparo; BITMAP *pantallaOculta; // El array que controla a todos los marcianos marciano marcianos[COLUMNASMARCIANOS][FILASMARCIANOS]; int fotograma = 1; int xNave = ANCHOPANTALLA / 2; int yNave = ALTOPANTALLA-16-MARGENINF; int xDisparo = 0; int yDisparo = 0; int numMarcianos = FILASMARCIANOS*COLUMNASMARCIANOS; int desplMarciano = INCREMX; int desplNave = INCREMX; int puedeMoverMarciano = 1; int puedeMoverNave = 1; int puedeMoverDisparo = 0; int disparoActivo = 0; int puntos = 0; int tecla; int salir = 0; /* -------------- Rutinas de temporizacin -------- */ // Contadores para temporizacin volatile int contadorN = 0; volatile int contadorM = 0; volatile int contadorD = 0; // La rutina que los aumenta cada cierto tiempo void aumentaContadores(void) { contadorN++; contadorM++; contadorD++; } END_OF_FUNCTION(aumentaContadores); /* -------------- Rutina de inicializacin -------- */ int inicializa() { int i,j; allegro_init(); // Inicializamos Allegro install_keyboard(); install_timer(); // Intentamos entrar a modo grafico if (set_gfx_mode(GFX_SAFE, ANCHOPANTALLA, ALTOPANTALLA, 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; } // e intentamos abrir imgenes imagen = load_pcx("spr_inv.pcx", pal); if (!imagen) { set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); allegro_message("No se ha podido abrir la imagen\n"); return 1; } set_palette(pal); // Ahora reservo espacio para los otros sprites nave = create_bitmap(16, 16); marciano1 = create_bitmap(16, 16); marciano2 = create_bitmap(16, 16); disparo = create_bitmap(8, 8); // Y los extraigo de la imagen "grande" blit(imagen, nave // bitmaps de origen y destino , 32, 32 // coordenadas de origen , 0, 0 // posicin de destino , 16, 16); // anchura y altura blit(imagen, marciano1, 0, 32, 0, 0, 16, 16); blit(imagen, marciano2, 16, 32, 0, 0, 16, 16); blit(imagen, disparo, 72, 32, 0, 0, 8, 8); // Valores iniciales de los marcianos for (i=0; i<COLUMNASMARCIANOS; i++) for (j=0; j<FILASMARCIANOS; j++) { marcianos[i][j].posX = MARGENIZQDO + i*DISTANCIAXMARCIANOS; marcianos[i][j].posY = MARGENSUP + j*DISTANCIAYMARCIANOS; marcianos[i][j].activo = 1; } // Pantalla oculta para evitar parpadeos // (doble buffer) pantallaOculta = create_bitmap(320, 200); // Rutinas de temporizacin // Bloqueamos las variables y la funcin del temporizador LOCK_VARIABLE( contadorN ); LOCK_VARIABLE( contadorM ); LOCK_VARIABLE( contadorD ); LOCK_FUNCTION( aumentaContadores ); // Y ponemos el temporizador en marcha: cada 10 milisegundos // (cada centsima de segundo) install_int(aumentaContadores, 10); // Y termino indicando que no ha habido errores return 0; } /* -------------- Rutina de mover marciano -------- */ int mueveMarciano() { int primeraX = ANCHOPANTALLA; int ultimaX = 0; int i,j; if (fotograma==1) fotograma = 2; else fotograma = 1; // Calculo nueva posicin for (i=0; i<COLUMNASMARCIANOS; i++) for (j=0; j<FILASMARCIANOS; j++) { marcianos[i][j].posX += desplMarciano; // Y veo si es el ms a la derecha o a la izqda if ((marcianos[i][j].activo) && (marcianos[i][j].posX > ultimaX)) ultimaX = marcianos[i][j].posX; if ((marcianos[i][j].activo) && (marcianos[i][j].posX < primeraX)) primeraX = marcianos[i][j].posX; } // Compruebo si debe bajar if ((ultimaX > ANCHOPANTALLA-16-MARGENDCHO) || (primeraX < MARGENIZQDO)) { desplMarciano = -desplMarciano; // Direccin contraria for (i=0; i<COLUMNASMARCIANOS; i++) for (j=0; j<FILASMARCIANOS; j++) { marcianos[i][j].posY += INCREMY; // Y bajo una lnea } } // O si se ha "invadido", partida acabada) for (i=0; i<COLUMNASMARCIANOS; i++) for (j=0; j<FILASMARCIANOS; j++) { if ((marcianos[i][j].activo) && (marcianos[i][j].posY > ALTOPANTALLA-20-MARGENINF)) { /*textprintf(screen, font, 10, 10, palette_color[11], "Invadido!");*/ salir = 1; // Y se acab } } } /* -------------- Rutina de mover disparo -------- */ int mueveDisparo() { int i,j; // Calculo nueva posicion yDisparo -= 4; // Compruebo si ha chocado for (i=0; i<COLUMNASMARCIANOS; i++) for (j=0; j<FILASMARCIANOS; j++) if (marcianos[i][j].activo) { if ((xDisparo >= marcianos[i][j].posX) && (xDisparo <= marcianos[i][j].posX + 8) && (yDisparo >= marcianos[i][j].posY) && (yDisparo <= marcianos[i][j].posY + 8)) { //textprintf(screen, font, xDisparo, yDisparo, // palette_color[11], "Boom!"); marcianos[i][j].activo = 0; disparoActivo = 0; puntos += 10; numMarcianos --; if (numMarcianos < 1) { /*textprintf(screen, font, 10, 10, palette_color[11], "Partida ganada!");*/ salir = 1; // Y se acab } } } // O si ha llegado arriba if (yDisparo < MARGENSUP-8) { xDisparo = 0; yDisparo = 0; puedeMoverDisparo = 0; disparoActivo = 0; } } /* ------------------------------------------------ */ /* */ /* -------------- Cuerpo del programa ------------- */ int main() { int i,j; // Intento inicializar if (inicializa() != 0) exit(1); do { // Parte que se repite hasta pulsar tecla // Slo se pueden mover tras un cierto tiempo if (contadorM >= RETARDOCM) { puedeMoverMarciano = 1; contadorM = 0; } if (contadorN >= RETARDOCN) { puedeMoverNave = 1; contadorN = 0; } if ((contadorD >= RETARDOCD) && (disparoActivo)) { puedeMoverDisparo = 1; contadorD = 0; } //if (puedeMoverMarciano || puedeMoverNave || puedeMoverDisparo) // Sincronizo con el barrido para evitar // parpadeos, borro la pantalla y dibujo la nave clear_bitmap(pantallaOculta); draw_sprite(pantallaOculta, nave, xNave, yNave); // Dibujo un marciano u otro, alternando for (i=0; i<COLUMNASMARCIANOS; i++) for (j=0; j<FILASMARCIANOS; j++) if (marcianos[i][j].activo) if (fotograma==1) draw_sprite(pantallaOculta, marciano1, marcianos[i][j].posX, marcianos[i][j].posY); else draw_sprite(pantallaOculta, marciano2, marcianos[i][j].posX, marcianos[i][j].posY); // Dibujo el disparo if (disparoActivo) draw_sprite(pantallaOculta, disparo, xDisparo, yDisparo); // Escribo la puntuacin textprintf(pantallaOculta, font, 100, 1, palette_color[11], "Puntos: %d",puntos); // Sincronizo con el barrido para evitar parpadeos // y vuelco la pantalla oculta vsync(); blit(pantallaOculta, screen, 0, 0, 0, 0, ANCHOPANTALLA, ALTOPANTALLA); // Compruebo teclas pulsadas para salir // o mover la nave if (keypressed()) { tecla = readkey() >> 8; if ( tecla == KEY_ESC) salir = 1; if (puedeMoverNave) { if (( tecla == KEY_RIGHT) && (xNave < ANCHOPANTALLA-16-MARGENDCHO)) { xNave += INCREMX; puedeMoverNave = 0; } if (( tecla == KEY_LEFT) && (xNave > MARGENIZQDO+16)) { xNave -= INCREMX; puedeMoverNave = 0; } if (( tecla == KEY_SPACE) && (!disparoActivo)) { disparoActivo = 1; puedeMoverDisparo = 1; contadorD = 0; xDisparo = xNave; yDisparo = yNave-2; } clear_keybuf(); } } // Y calculo la nueva posicin de los marcianos if (puedeMoverMarciano) { mueveMarciano(); puedeMoverMarciano = 0; } // Y anlogo para el disparo if (puedeMoverDisparo) { mueveDisparo(); puedeMoverDisparo = 0; } } while (!salir); textprintf(screen, font, 1, 1, palette_color[11], "Partida terminada. "); destroy_bitmap(imagen); destroy_bitmap(nave); destroy_bitmap(marciano1); destroy_bitmap(marciano2); destroy_bitmap(disparo); destroy_bitmap(pantallaOculta); readkey(); rest(2000); return 0; } /* Termino con la "macro" que me pide Allegro */ END_OF_MAIN();
Ahora añadamos disparos de los enemigos...