19. Moviendo una nave y un enemigo a la vez con velocidades distintas. Octavo juego (aproximación "c"): Marciano 3. (*)

La idea de esta tercera aproximación es:

  • Añadiremos nuestra nave, capaz de moverse de lado a lado, a velocidad distinta. Tampoco deberá poderse salir por los lados.


El hecho de que nuestra nave se mueva, y que no pueda salir de la pantalla, son cosas que ya sabemos hacer. La dificultad está en que se mueva a la vez que el marciano y a velocidad distinta de la de éste. Ahora no podremos usar "pausa" porque el programa no deberá esperar, lo que deberemos es comprobar continuamente si ha llegado el momento de mover alguno de los dos personajes.

La idea básica sería algo como:

repetir

  si hayQueRedibujarMarciano
    dibujar marciano
    hayQueRedibujarMarciano = FALSO

  si hayQueRedibujarNave
    dibujar nave
    hayQueRedibujarNave = FALSO

  si tiempoTranscurridoNave = tiempoHastaMoverNave
    comprobar si se ha pulsado alguna tecla
    calcular nueva posición de nave
    hayQueRedibujarNave = VERDADERO

  si tiempoTranscurridoMarciano = tiempoHastaMoverMarciano
    calcular nueva posición de marciano
    hayQueRedibujarMarciano = VERDADERO

hasta que se pulse ESC

No debería tener problemas. Está claro que frases como "calcular nueva posición de marciano" equivalen a varias órdenes (de hecho, a casi toda la aproximación anterior) y que expresiones como "tiempoTranscurridoMarciano" serían simplemente una resta entre dos tiempos, algo así como "HoraActual - HoraUltimoMovimientoMarciano" si tenemos un reloj disponible, o algo como "contador > RetardoEsperado" si tenemos que usar un contador, como en Allegro.

Marciano y nave

Se puede hacer así (como siempre, no es la única forma posible de plantearlo, ni siquiera la mejor, pero espero que al menos sea fácil de seguir):

/*----------------------------*/
/*  Intro a la programac de   */
/*  juegos, por Nacho Cabanes */
/*                            */
/*    ipj19c.c                */
/*                            */
/*  Decimosexto ejemplo:      */
/*    Un marciano y una nave  */
/*    movindose de forma     */
/*    independiente           */
/*                            */
/*  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  */
/*----------------------------*/
 
/* -------------------
 
Planteamiento de esta aproximacin:
 
repetir
 
  si hayQueRedibujarMarciano
    dibujar marciano
    hayQueRedibujarMarciano = FALSO
 
  si hayQueRedibujarNave
    dibujar nave
    hayQueRedibujarNave = FALSO
 
  si tiempoTranscurridoNave = tiempoHastaMoverNave
    comprobar si se ha pulsado alguna tecla
    calcular nueva posicin de nave
    hayQueRedibujarNave = VERDADERO
 
  si tiempoTranscurridoMarciano = tiempoHastaMoverMarciano
    calcular nueva posicin de marciano
    hayQueRedibujarMarciano = VERDADERO
 
hasta que se pulse ESC
 
---------------
 
Planteamiento de la anterior:
 
cargar imagen 1 y 2 del marciano
entrar a modo grfico
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 lnea 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 centsimas de segundo
#define RETARDOCN 10
#define RETARDOCM 25
 
#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;
 
int fotograma = 1;
 
int xMarciano = MARGENIZQDO;
int yMarciano = MARGENSUP;
int xNave = ANCHOPANTALLA / 2;
int yNave = ALTOPANTALLA-16-MARGENINF;
 
int desplMarciano = INCREMX;
int desplNave = INCREMX;
 
int puedeMoverMarciano = 1;
int puedeMoverNave = 1;
int tecla;
 
int salir = 0;
 
 
/* -------------- Rutinas de temporizacin -------- */
// Contadores para temporizacin
volatile int contadorN = 0;
volatile int contadorM = 0;
 
// La rutina que lo aumenta cada cierto tiempo
void aumentaContadores(void)
{
   contadorN++;
   contadorM++;
}
 
END_OF_FUNCTION(aumentaContadores);
 
 
 
 
/* -------------- Rutina de inicializacin -------- */
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 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);
 
    // 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);
 
   // Rutinas de temporizacin 
   // Bloqueamos las variables y la funcin del temporizador
   LOCK_VARIABLE( contadorN );
   LOCK_VARIABLE( contadorM );
   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 inicializacin -------- */
int mueveMarciano()
{
    // Finalmente, calculo nueva posicin
    xMarciano += desplMarciano;
    // Compruebo si debe bajar
    if ((xMarciano > ANCHOPANTALLA-16-MARGENDCHO)
        || (xMarciano < MARGENIZQDO))
    {
        desplMarciano = -desplMarciano; // Direccin contraria
        yMarciano += INCREMY;           // Y bajo una lnea
    }
    // O si debe volver arriba
    if (yMarciano > ALTOPANTALLA-16-MARGENINF) {
        xMarciano = MARGENIZQDO;
        yMarciano = MARGENSUP;
        desplMarciano = INCREMX;
    }
}
 
/* ------------------------------------------------ */
/*                                                  */
/* -------------- Cuerpo del programa ------------- */
 
int main()
{
 
    // 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 (puedeMoverMarciano || puedeMoverNave) {
 
            // 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;
                        }
                    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 posicin si corresponde
            if (puedeMoverMarciano) {
                mueveMarciano();
                puedeMoverMarciano = 0;
            }
 
        }
 
    } while (!salir);
 
    destroy_bitmap(imagen);
    readkey();
    return 0;
 
}
 
            /* Termino con la "macro" que me pide Allegro */
END_OF_MAIN();
 

Ahora añadamos el disparo y la detección de colisiones...