Versión: 0.47, de 10-Jul-2011


21. Moviendo varios marcianos a la vez. Octavo juego(aproximación "e"): Marciano 5. (*)

Si se ha entendido la aproximación anterior al juego, la versión "completa" no aporta muchas dificultades. Apenas dos importantes:

Por eso, haremos nuevamente varias aproximaciones sucesivas hasta tener un juego "razonablemente jugable". 

En este primer acercamiento, simplemente mostrará varios enemigos moviéndose a la vez.

Las diferencias entre tener un único enemigo o varios no son muchas, pero hay que tener varias cosas en cuenta:


Vamos poco a poco:

El hecho de dibujar varios marcianos se podría resolver simplemente con algo como

 
  for (i=0; i<COLUMNASMARCIANOS; i++)  
      for (j=0; j<FILASMARCIANOS; j++) { 
              draw_sprite(screen, marciano1,  
                  xMarciano + i*DISTANCIAXMARCIANOS, 
                  yMarciano + j*DISTANCIAYMARCIANOS); 
              ... 
 

Donde las constantes son las que indican el número de marcianos en vertical y en horizontal, así como la distancia entre ellos:

 
  #define FILASMARCIANOS 5 
  #define COLUMNASMARCIANOS 11 
  #define DISTANCIAXMARCIANOS 20 
  #define DISTANCIAYMARCIANOS 12
 

Además, los marcianos deberán avanzar menos hacia la derecha que si estuvieran solos, por ejemplo con:

 
  if ((xMarciano > ANCHOPANTALLA-16-MARGENDCHO-COLUMNASMARCIANOS*DISTANCIAXMARCIANOS)  
      || (xMarciano < MARGENIZQDO)) 
  { 
      desplMarciano = -desplMarciano; // Dirección contraria 
      yMarciano += INCREMY;           // Y bajo una línea 
  } 
 

Pero este planteamiento no hace que sea fácil calcular cuando se ha matado a un marciano concreto, ni cuando el bloque de marcianos debe avanzar más hacia la derecha o hacia la izquierda. Una opción más razonable sería crear una "estructura" (nos vamos acercando a los objetos) que representa a cada marciano, con datos como su posición y si está activo:

 
  // Datos de cada marciano 
  typedef struct { 
      int posX; 
      int posY; 
      int activo; 
  } marciano; 
 
  // El array que controla a todos los marcianos 
  marciano marcianos[COLUMNASMARCIANOS][FILASMARCIANOS]; 
 

Y apenas cambia un poco la forma de dibujar a los marcianos, que ahora se hará dentro de un doble bucle "for", para recorrer todos, y comprobando si están activos:

 
  // Dibujo un marciano u otro, alternando 
  if (puedeMoverMarciano) { 
      for (i=0; i<COLUMNASMARCIANOS; i++)  
          for (j=0; j<FILASMARCIANOS; j++) 
              if (marcianos[i][j].activo) 
              { 
                  if (fotograma==1) { 
                      draw_sprite(screen, marciano1,  
                          marcianos[i][j].posX, marcianos[i][j].posY); 
                      fotograma = 2; 
                  } else { 
                      draw_sprite(screen, marciano2, 
                          marcianos[i][j].posX, marcianos[i][j].posY); 
                      fotograma = 1;  
                  } 
              }  
      } 
 

o la de comprobar si un disparo ha acertado, con los mismos cambios:

 
  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 hasta donde deben moverse los marcianos, porque habrá que calcular la "primera x" (la del marciano más a la izquierda) y la "última x" (la del marciano más a la derecha):

 
  // Calculo nueva posición 
  for (i=0; i<COLUMNASMARCIANOS; i++)  
      for (j=0; j<FILASMARCIANOS; j++) { 
          marcianos[i][j].posX += desplMarciano; 
          // Y veo si es el más 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; // Dirección contraria 
      for (i=0; i<COLUMNASMARCIANOS; i++)  
          for (j=0; j<FILASMARCIANOS; j++) { 
              marcianos[i][j].posY += INCREMY;           // Y bajo una línea 
          } 
  } 
 

IPJ - Tema 21

De paso, he añadido un par de refinamientos: una puntuación (10 puntos por cada marciano eliminado) y el final de la partida si los marcianos nos invaden (si alcanzan la parte inferior de la pantalla).
 

El problema es que dibujamos unas 57 figuras en pantalla cada vez, lo que provoca un serio problema de parpadeo, que corregiremos en el próximo apartado. Aun así, para quien quiera ver cómo sería el fuente completo de esta versión preliminar, aquí está:

/*------------------------------*/
/*  Intro a la programac de     */
/*  juegos, por Nacho Cabanes   */
/*                              */
/*    ipj21c.c                  */
/*                              */
/*  Ejemplo:                    */
/*    Varios marcianos y una    */
/*    nave moviéndose de        */
/*    forma independiente; la   */
/*    nave puede disparar y     */
/*    "matar" a los marcianos   */
/*    Problema: 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    */
/*------------------------------*/
 
 
 
#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 35
// Y también el del disparo
#define RETARDOCD 15
 
#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;
 
// 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 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()
{
    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 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);
 
    // 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;
            }
 
   // 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()
{
    int primeraX = ANCHOPANTALLA;
    int ultimaX = 0;
    int i,j;
 
    if (fotograma==1) fotograma = 2;
        else fotograma = 1;
 
        // Calculo nueva posición
    for (i=0; i<COLUMNASMARCIANOS; i++)
        for (j=0; j<FILASMARCIANOS; j++) {
            marcianos[i][j].posX += desplMarciano;
            // Y veo si es el más 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; // Dirección contraria
        for (i=0; i<COLUMNASMARCIANOS; i++)
            for (j=0; j<FILASMARCIANOS; j++) {
                marcianos[i][j].posY += INCREMY;           // Y bajo una línea
            }
    }
 
    // 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
 
        // 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;
        }
 
 
        // 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();
                }
        }
 
        if (puedeMoverMarciano || puedeMoverNave || puedeMoverDisparo)
        {
 
            // 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 (puedeMoverMarciano) {
                for (i=0; i<COLUMNASMARCIANOS; i++)
                    for (j=0; j<FILASMARCIANOS; j++)
                        if (marcianos[i][j].activo)
                        {
                            if (fotograma==1)
                                draw_sprite(screen, marciano1,
                                    marcianos[i][j].posX, marcianos[i][j].posY);
                            else
                                draw_sprite(screen, marciano2,
                                    marcianos[i][j].posX, marcianos[i][j].posY);
                        }
                }
 
            // 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);
 
            textprintf(screen, font, 100, 1,
                palette_color[11], "Puntos: %d",puntos);
        }
 
    } 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);
    readkey();
    rest(2000);
    return 0;
 
}
 
            /* Termino con la "macro" que me pide Allegro */
END_OF_MAIN();
 

Eliminemos ese parpadeo...

Anterior Siguiente