20. Incluyendo un disparo y comprobación de colisiones. Octavo juego (aproximación "d"): Marciano 4. (*)

La idea de esta cuarta aproximación es:

  • Incluiremos también la posibilidad de disparar, el movimiento vertical del disparo y la detección de colisiones entre el disparo y el enemigo. El disparo "desaparecerá" cuando golpee al enemigo o llegue a la parte superior de la pantalla, y sólo entonces se podrá volver a disparar. 


Sin grandes novedades. Un tercer "personaje" a controlar, el disparo, que no añade casi ninguna complicación, salvo por el hecho de que no siempre está "activo" (puede haber momentos en los que no estemos disparando). La novedad es cómo comprobar si el disparo ha impactado en la nave enemiga. Una forma muy sencilla de conseguirlo puede ser simplemente comparar la posición del disparo y la del marciano. Como también sabemos el "tamaño" del marciano (su anchura y su altura), podemos saber si el disparo ha acertado (si su coordenada X está entre XMarciano y XMarciano+AnchoMarciano, y lo mismo para la coordenada Y).

 

Marciano, nave y disparo

Es muy similar a la aproximación anterior, aunque ligeramente más largo:

/*----------------------------*/
/*  Intro a la programac de   */
/*  juegos, por Nacho Cabanes */
/*                            */
/*    ipj20c.c                */
/*                            */
/*  Ejemplo:                  */
/*    Un marciano y una nave  */
/*    moviéndose de forma     */
/*    independiente; la nave  */
/*    puede disparar y        */
/*    "matar" al marciano     */
/*                            */
/*  Comprobado con:           */
/*  - Djgpp 2.03 (gcc 3.2)    */
/*    y Allegro 4.02 - MsDos  */
/*----------------------------*/
 
/* -------------------
 
Planteamiento de esta aproximación:
 
repetir
 
  si hayQueRedibujarMarciano
    dibujar marciano
    hayQueRedibujarMarciano = FALSO
 
  si hayQueRedibujarNave
    dibujar nave
    hayQueRedibujarNave = FALSO
 
  si (hayQueRedibujarDisparo) y (disparoActivo)
    dibujar disparo
    hayQueRedibujarDisparo = FALSO
 
  si tiempoTranscurridoNave = tiempoHastaMoverNave
    comprobar si se ha pulsado alguna tecla
    calcular nueva posición de nave
    hayQueRedibujarNave = VERDADERO
    si teclaPulsada = teclaDisparo
      disparoActivo = VERDADERO
 
  si tiempoTranscurridoMarciano = tiempoHastaMoverMarciano
    calcular nueva posición de marciano
    hayQueRedibujarMarciano = VERDADERO
 
  si (tiempoTranscurridoDisparo = tiempoHastaMoverDisparo)
      y (disparoActivo)
    calcular nueva posición de disparo
    hayQueRedibujarDisparo = VERDADERO
 
  si disparoFueraDeLaPantalla
    disparoActivo = FALSO
 
  si colisionDisparoYMarciano
    finDeLaPartida
 
hasta que se pulse ESC
 
---------------
 
Planteamiento de la inicial:
 
cargar imagen 1 y 2 del marciano
entrar a modo gráfico
dibujar marciano en punto inicial
 
repetir
  aumentar posicion horizontal
  si se acerca a margen lateral de la pantalla
    cambiar sentido horizontal de avance
    bajar una línea en vertical
  si se acerca a parte inferior de la pantalla
    mover a parte superior
  aumentar contador de "fotogramas"
  si contador de fotogramas = numFotogramasPorImagen
    cambiar apariencia de marciano
    contador de fotogramas = 0
  redibujar pantalla
  pausa
hasta que se pulse ESC
 
 
------------------- */
 
 
#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 centésimas de segundo
#define RETARDOCN 10
#define RETARDOCM 25
// Y también el del disparo
#define RETARDOCD 15
 
#define MARGENSUP   40
#define MARGENDCHO  30
#define MARGENIZQDO 30
#define MARGENINF   50
#define INCREMX      5
#define INCREMY     15
 
/* -------------- Variables globales -------------- */
PALETTE pal;
BITMAP *imagen;
BITMAP *nave;
BITMAP *marciano1;
BITMAP *marciano2;
BITMAP *disparo;
 
int fotograma = 1;
 
int xMarciano = MARGENIZQDO;
int yMarciano = MARGENSUP;
int xNave = ANCHOPANTALLA / 2;
int yNave = ALTOPANTALLA-16-MARGENINF;
int xDisparo = 0;
int yDisparo = 0;
 
 
int desplMarciano = INCREMX;
int desplNave = INCREMX;
 
int puedeMoverMarciano = 1;
int puedeMoverNave = 1;
int puedeMoverDisparo = 0;
int disparoActivo = 0;
int tecla;
 
int salir = 0;
 
 
/* -------------- Rutinas de temporización -------- */
// Contadores para temporización
volatile int contadorN = 0;
volatile int contadorM = 0;
volatile int contadorD = 0;
 
// La rutina que lo aumenta cada cierto tiempo
void aumentaContadores(void)
{
   contadorN++;
   contadorM++;
   contadorD++;
}
 
END_OF_FUNCTION(aumentaContadores);
 
 
 
 
/* -------------- Rutina de inicialización -------- */
int inicializa()
{
    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 imágenes
    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             // posición 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);
 
   // Rutinas de temporización 
   // Bloqueamos las variables y la función del temporizador
   LOCK_VARIABLE( contadorN );
   LOCK_VARIABLE( contadorM );
   LOCK_FUNCTION( aumentaContadores );
 
   // Y ponemos el temporizador en marcha: cada 10 milisegundos
   // (cada centésima de segundo)
   install_int(aumentaContadores, 10);
 
   // Y termino indicando que no ha habido errores
   return 0;
}
 
/* -------------- Rutina de mover marciano -------- */
int mueveMarciano()
{
    // Calculo nueva posición
    xMarciano += desplMarciano;
    // Compruebo si debe bajar
    if ((xMarciano > ANCHOPANTALLA-16-MARGENDCHO)
        || (xMarciano < MARGENIZQDO))
    {
        desplMarciano = -desplMarciano; // Dirección contraria
        yMarciano += INCREMY;           // Y bajo una línea
    }
    // O si debe volver arriba
    if (yMarciano > ALTOPANTALLA-16-MARGENINF) {
        xMarciano = MARGENIZQDO;
        yMarciano = MARGENSUP;
        desplMarciano = INCREMX;
    }
}
 
/* -------------- Rutina de mover disparo -------- */
int mueveDisparo()
{
    // Calculo nueva posicion
    yDisparo -= 4;
    // Compruebo si ha chocado
    if ((xDisparo >= xMarciano)
          && (xDisparo <= xMarciano + 8)
          && (yDisparo >= yMarciano)
          && (yDisparo <= yMarciano + 8))
        {
        textprintf(screen, font, xDisparo, yDisparo,
            palette_color[11], "Boom!");
        readkey();
        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()
{
 
    // Intento inicializar
    if (inicializa() != 0)
        exit(1);
 
 
    do {  // Parte que se repite hasta pulsar tecla
 
        // Sólo 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)
            {
 
            // 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();
                    }
            }
 
            // Sincronizo con el barrido para evitar 
            // parpadeos, borro la pantalla y dibujo la nave
            vsync();
            clear_bitmap(screen);
            draw_sprite(screen, nave, xNave, yNave);
 
            // Dibujo un marciano u otro, alternando
            if (fotograma==1) {
                draw_sprite(screen, marciano1, xMarciano, yMarciano);
                if (puedeMoverMarciano)
                  fotograma = 2;
            } else {
                draw_sprite(screen, marciano2, xMarciano, yMarciano);
                if (puedeMoverMarciano)
                  fotograma = 1;
            }
 
            // Y calculo su nueva posición si corresponde
            if (puedeMoverMarciano) {
                mueveMarciano();
                puedeMoverMarciano = 0;
            }
 
            // Y análogo para el disparo
            if (puedeMoverDisparo)  {
                mueveDisparo();
                puedeMoverDisparo = 0;
            }
            if (disparoActivo)
              draw_sprite(screen, disparo, xDisparo, yDisparo);
 
        }
 
    } while (!salir);
 
    textprintf(screen, font, 1, 1,
            palette_color[11], "Partida terminada.");
    destroy_bitmap(imagen);
    readkey();
    return 0;
 
}
 
            /* Termino con la "macro" que me pide Allegro */
END_OF_MAIN();
 
 

Ahora incluyamos varios marcianos...