17. Cargar y mostrar imágenes. Octavo juego (aproximación "a"): Marciano 1 (*)

Antes de ver cómo mover a la vez varias figuras que hemos leido desde el disco... ¡deberíamos tener claro cómo leer esas figuras! 

Pues bien, en el apartado 15 hemos comentado la idea básica de cómo leer imágenes desde un fichero. Ahora vamos a llevarlo a la práctica. Además lo haremos como realmente se suele hacer. Me explico: si en nuestro juego hay 20 tipos de enemigos distintos, podríamos tener 20 ficheros de imágenes, cada uno de los cuales tendría guardada la apariencia de un enemigo. Pero eso no es lo habitual. Es más eficiente y más cómodo tener todas las imágenes en un solo fichero. En nuestro caso, podríamos crear una única imagen con esta apariencia:

Sprite de invaders

(Esta imagen está al triple de su tamaño real, para que se aprecie mejor).

En esta imagen tenemos más incluso de lo que usaremos por ahora:

  • Las cifras del 0 al 9, para mostrar la puntuación.
  • Los "letreros" que nos interesan en pantalla (SCORE=Puntuación, UP para formar 1UP= Primer jugador y 2UP=Segundo jugador, HI- para HI-SCORE= mejor puntuación).
  • Los primeros "personajes": las dos apariencias posibles del "marciano", la de nuestra nave, la de unas "torres" debajo de las que nos podríamos refugiar (no lo haremos por ahora), y la del disparo.


El "truco" para que nos sea cómodo consiste en que las imágenes que queremos mostrar tengan un tamaño fijo. Por razones "históricas" (limitaciones del hardware de antiguos ordenadores personales), es frecuente que ese tamaño sea un múltiplo de 8 píxeles: 8, 16, 32 o incluso 64. Si sabemos que cada pequeño "sprite" tiene 16 puntos de ancho y 16 puntos de alto, es facilísimo leer el sprite que hemos dibujado en la 4ª fila y la 8ª columna de nuestra imagen, por ejemplo.

En mi caso todas las figuras de la imagen anterior tiene 16 píxeles de alto. Las letras, cifras y el disparo tienen 8 píxeles de ancho, mientras que el marciano, la nave y la torre defensiva tienen 16 pixeles de ancho. Por tanto, el sprite de la nave comenzará en la posición (32, 32), porque tiene 2 figuras de 16 píxeles a su izquierda y dos filas de 16 píxeles por encima.

Pues vamos con ello...

Un ejemplo que mostrara esta imagen al completo, la nave en la parte inferior de la pantalla y el marciano cambiando de forma en la parte superior de la pantalla, podría ser:


/*----------------------------*/
/*  Intro a la programac de   */
/*  juegos, por Nacho Cabanes */
/*                            */
/*    ipj17c.c                */
/*                            */
/*    Leer imgenes desde     */
/*    un fichero              */
/*                            */
/*  Comprobado con:           */
/*  - Djgpp 2.03 (gcc 3.2)    */
/*    y Allegro 4.02 - MsDos  */
/*----------------------------*/
 
#include <allegro.h>
 
 
/* -------------- Constantes globales ------------- */
#define ANCHOPANTALLA 320
#define ALTOPANTALLA 200
#define RETARDO 300
 
 
/* -------------- Variables globales -------------- */
PALETTE pal;
BITMAP *imagen;
BITMAP *nave;
BITMAP *marciano1;
BITMAP *marciano2;
 
 
/* -------------- 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;
    }
    set_palette(pal);
                           // 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;
    }
 
    // 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);
 
 
    // Y termino indicando que no ha habido errores
    return 0;
}
 
 
 
/* ------------------------------------------------ */
/*                                                  */
/* -------------- Cuerpo del programa ------------- */
 
int main()
{
    int fotograma = 1;
 
    // Intento inicializar
    if (inicializa() != 0)
        exit(1);
 
 
    do {  // Parte que se repite hasta pulsar tecla
 
        clear_bitmap(screen);
 
        // Dibujo la figura y la nave
        draw_sprite(screen, imagen, 120, 80);
        draw_sprite(screen, nave, 70, 150);
 
        // Dibujo un marciano u otro, alternando
        if (fotograma==1) {
           draw_sprite(screen, marciano1, 250, 40);
           fotograma = 2;
        } else {
           draw_sprite(screen, marciano2, 250, 40);
           fotograma = 1;
        }
        // Y espero un poco
        rest(RETARDO);
 
    } while (!keypressed());
 
    destroy_bitmap(imagen);
    readkey();
    return 0;
 
}
 
            /* Termino con la "macro" que me pide Allegro */
END_OF_MAIN();
 


(Por cierto, si quieres usar exactamente la misma imagen que yo, y ya convertida a formato PCX, la tienes aquí).

Debería resultar fácil de seguir. Para volcar un trozo de un sprite a otro se usa la orden "blit", a la que se le indica el sprite de origen y el de destino, la posición desde la que queremos comenzar a leer, la posición de destino, la anchura y la altura. Para que un sprite "cambie de forma" basta con leer varias imágenes distintas para él, e ir cambiando de una a otra según la circunstancia (puede ser un simple contador, como en este caso, o puede modificarse en casos especiales, como cuando estalle el marciano si nuestro disparo impacta en él).

Ahora vamos a mover la nave enemiga...

(Pascal y Java: Aun no completo - Pronto estará disponible ) (*)