8. Cómo crear figuras multicolor que se muevan. Cuarto juego (aproximación "b"): Miniserpiente 2.
Contenido de este apartado:
8.1. Ideas generales.
Ya sabemos como usar "Mapas" para memorizar los obstáculos y "premios" que el personaje de nuestro juego debe esquivar o alcanzar. Pero nuestros personajes son "pobres", con una presentación muy poco lucida. Va llegando el momento de hacer personajes un poco más vistosos.
Aprenderemos a crear figuras multicolor que se muevan. Además estas figuras serán "transparentes": podrán tener "huecos" alrededor o en su interior y a través de estos huecos deberá verse el fondo. Estas figuras reciben el nombre de "sprites".
En Allegro tenemos rutinas para el manejo de Sprites:
draw_sprite(screen, figura, posicionX, posicionY);
En Pascal también tendríamos algo parecido con la orden "putimage", pero seremos más atrevidos: imitaremos lo que haría la orden "draw_sprite" con una creada por nosotros, para que se entienda mejor el funcionamiento, a cambio de que el resultado sea algo más lento (no tendremos muchas figuras en pantalla, no será grave).
El pseudocódigo de lo que hará esta función (muy parecido a lo que haremos en Pascal) sería:
procedimiento
dibujar_sprite(imagen, posicX, posicY);
para i = 1 hasta ANCHOSPRITE
para j = 1 hasta ALTOSPRITE
si imagen[j,i] <> 0 entonces
dibujar_punto(x+i-1, y+j-1,
imagen[j,i]);
Simplemente dibujamos todos los puntos menos los que sean del color que hayamos considerado transparente (en nuestro caso, el 0).
Y los sprites los vamos a crear desde dentro de nuestro programa (más adelante veremos cómo leer imágenes desde ficheros). Simplemente será un array de dos dimensiones, que indicará el color de cada uno de los puntos que forman la figura:
spriteLadrillo =
{{0,2,2,2,2,2,2,2,2,0},
{2,1,1,1,1,1,1,1,1,2},
{2,1,1,1,1,1,1,1,1,2},
{2,1,1,1,1,1,1,1,1,2},
{2,1,1,1,1,1,1,1,1,2},
{2,1,1,1,1,1,1,1,3,2},
{2,1,1,1,1,1,1,3,3,2},
{2,1,1,1,1,1,3,3,2,2},
{2,2,2,2,2,2,2,2,2,0}
};
El resto del fuente es idéntico al que hicimos en el apartado anterior. La apariencia que buscamos (algo mejor que la anterior, pero todavía nada espectacular) es
8.2 Miniserpiente 2 en C.
La única diferencia importante con la versión 1 es el manejo de los sprites:/*----------------------------*/ /* Intro a la programac de */ /* juegos, por Nacho Cabanes */ /* */ /* ipj08c.c */ /* */ /* Octavo ejemplo: juego de */ /* "miniSerpiente" (aprox B) */ /* */ /* Comprobado con: */ /* - MinGW DevStudio 2.05 */ /* (gcc 3.4.2) y Allegro */ /* 4.03, Windows XP */ /*----------------------------*/ #include <allegro.h> /* Posiciones X e Y iniciales */ #define POS_X_INI 16 #define POS_Y_INI 10 #define INC_X_INI 1 #define INC_Y_INI 0 /* Pausa en milisegundos entre un "fotograma" y otro */ #define PAUSA 350 /* Teclas predefinidas */ #define TEC_ARRIBA KEY_E #define TEC_ABAJO KEY_X #define TEC_IZQDA KEY_S #define TEC_DCHA KEY_D int posX, posY; /* Posicion actual */ int incX, incY; /* Incremento de la posicion */ /* Terminado: Si ha chocado o comida todas las frutas */ int terminado; /* La tecla pulsada */ int tecla; /* Escala: relacion entre tamaño de mapa y de pantalla */ #define ESCALA 10 /* Ancho y alto de los sprites */ #define ANCHOSPRITE 10 #define ALTOSPRITE 10 /* Y el mapa que representa a la pantalla */ /* Como usaremos modo grafico de 320x200 puntos */ /* y una escala de 10, el tablero medira 32x20 */ #define MAXFILAS 20 #define MAXCOLS 33 char mapa[MAXFILAS][MAXCOLS]={ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "X X X", "X F X X", "X F X F X", "X XXXXX X X", "X X X X", "X X X X X", "X X X X XXXX", "X X X X", "X X X X", "X X X X", "X F X X", "X X X", "X X F X", "X X X X", "X X X X", "X X F X X", "X F X X X", "X X F X", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }; int numFrutas = 8; /* Nuestros sprites */ BITMAP *ladrilloFondo, *comida, *jugador; typedef char tipoSprite[ANCHOSPRITE][ALTOSPRITE]; /* El sprite en si: matriz de 10x10 bytes */ tipoSprite spriteLadrillo = {{0,2,2,2,2,2,2,2,2,0}, {2,1,1,1,1,1,1,1,1,2}, {2,1,1,1,1,1,1,1,1,2}, {2,1,1,1,1,1,1,1,1,2}, {2,1,1,1,1,1,1,1,1,2}, {2,1,1,1,1,1,1,1,3,2}, {2,1,1,1,1,1,1,3,3,2}, {2,1,1,1,1,1,3,3,2,2}, {2,2,2,2,2,2,2,2,2,0} }; tipoSprite spriteComida = {{0,0,0,2,0,0,0,0,0,0}, {0,0,2,2,0,0,2,2,0,0}, {0,4,4,4,2,2,4,4,0,0}, {4,4,4,4,4,2,4,4,4,0}, {4,4,4,4,4,4,4,4,4,0}, {4,4,4,4,4,4,4,4,4,0}, {4,4,4,4,4,4,4,4,4,0}, {4,4,4,4,4,4,4,4,4,0}, {0,4,4,4,4,4,4,4,0,0} }; tipoSprite spriteJugador = {{0,0,3,3,3,3,3,0,0,0}, {0,3,1,1,1,1,1,3,0,0}, {3,1,1,1,1,1,1,1,3,0}, {3,1,1,1,1,1,1,1,3,0}, {3,1,1,1,1,1,1,1,3,0}, {3,1,1,1,1,1,1,1,3,0}, {0,3,1,1,1,1,1,3,0,0}, {0,0,3,3,3,3,3,0,0,0} }; /* -------------- Rutina de crear los sprites ------------- */ void creaSprites() { int i, j; ladrilloFondo = create_bitmap(10, 10); clear_bitmap(ladrilloFondo); for(i=0; i<ANCHOSPRITE; i++) for (j=0; j<ALTOSPRITE; j++) putpixel(ladrilloFondo, i, j, palette_color[ spriteLadrillo[j][i] ]); comida = create_bitmap(10, 10); clear_bitmap(comida); for(i=0; i<ANCHOSPRITE; i++) for (j=0; j<ALTOSPRITE; j++) putpixel(comida, i, j, palette_color[ spriteComida[j][i] ]); jugador = create_bitmap(10, 10); clear_bitmap(jugador); for(i=0; i<ANCHOSPRITE; i++) for (j=0; j<ALTOSPRITE; j++) putpixel(jugador, i, j, palette_color[ spriteJugador[j][i] ]); } /* -------------- Rutina de dibujar el fondo ------------- */ void dibujaFondo() { int i, j; clear_bitmap(screen); for(i=0; i<MAXCOLS; i++) for (j=0; j<MAXFILAS; j++) { if (mapa[j][i] == 'X') draw_sprite(screen, ladrilloFondo, i*ESCALA, j*ESCALA); if (mapa[j][i] == 'F') draw_sprite(screen, comida, i*ESCALA, j*ESCALA); } } /* ------------------------------------------------ */ /* */ /* -------------- Cuerpo del programa ------------- */ int main() { allegro_init(); /* Inicializamos Allegro */ install_keyboard(); install_timer(); /* Intentamos entrar a modo grafico */ if (set_gfx_mode(GFX_SAFE, 320, 200, 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; } /* ----------------------- Si todo ha ido bien: empezamos */ creaSprites(); dibujaFondo(); /* Valores iniciales */ posX = POS_X_INI; posY = POS_Y_INI; incX = INC_X_INI; incY = INC_Y_INI; /* Parte repetitiva: */ do { dibujaFondo(); draw_sprite (screen, jugador, posX*ESCALA, posY*ESCALA); terminado = FALSE; /* Si paso por una fruta: la borro y falta una menos */ if (mapa[posY][posX] == 'F') { mapa[posY][posX] = ' '; numFrutas --; if (numFrutas == 0) { textout(screen, font, "Ganaste!", 100, 90, palette_color[14]); terminado = TRUE; } } /* Si choco con la pared, se acabo */ if (mapa[posY][posX] == 'X') { textout(screen, font, "Chocaste!", 100, 90, palette_color[13]); terminado = TRUE; } if (terminado) break; /* Compruebo si se ha pulsado alguna tecla */ if ( keypressed() ) { tecla = readkey() >> 8; switch (tecla) { case TEC_ARRIBA: incX = 0; incY = -1; break; case TEC_ABAJO: incX = 0; incY = 1; break; case TEC_IZQDA: incX = -1; incY = 0; break; case TEC_DCHA: incX = 1; incY = 0; break; } } posX += incX; posY += incY; /* Pequeña pausa antes de seguir */ rest ( PAUSA ); } while (TRUE); /* Repetimos indefininamente */ /* (la condición de salida la comprobamos "dentro") */ readkey(); return 0; } /* Termino con la "macro" que me pide Allegro */ END_OF_MAIN();
8.3. Miniserpiente 2 en Pascal.
Similar, pero con la rutina de dibujar sprites desarrollada, y con la misma apariencia:(*----------------------------*) (* Intro a la programac de *) (* juegos, por Nacho Cabanes *) (* *) (* IPJ08P.PAS *) (* *) (* Octavo ejemplo: juego de *) (* 'miniSerpiente'2 *) (* *) (* Comprobado con: *) (* - FreePascal 1.06 (Dos) *) (* - FreePascal 2.0 -Windows *) (*----------------------------*) uses graph, crt; (* Cambiar por "uses wincrt, ..." bajo Windows *) (* Posiciones X e Y iniciales *) const POS_X_INI = 17; POS_Y_INI = 11; INC_X_INI = 1; INC_Y_INI = 0; (* Pausa en milisegundos entre un 'fotograma' y otro *) PAUSA = 350; (* Teclas predefinidas *) TEC_ARRIBA = 'E'; TEC_ABAJO = 'X'; TEC_IZQDA = 'S'; TEC_DCHA = 'D'; var posX, posY: word; (* Posicion actual *) incX, incY: integer; (* Incremento de la posicion *) (* Terminado: Si ha chocado o comida todas las frutas *) terminado: boolean; (* La tecla pulsada *) tecla: char; (* Escala: relacion entre tamao de mapa y de pantalla *) const ESCALA = 10; (* Ancho y alto de los sprites *) ANCHOSPRITE = 10; ALTOSPRITE = 10; (* Y el mapa que representa a la pantalla *) (* Como usaremos modo grafico de 320x200 puntos *) (* y una escala de 10, el tablero medira 32x20 *) MAXFILAS = 20; MAXCOLS = 32; mapa: array[1..MAXFILAS, 1..MAXCOLS] of char = ( 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', 'X X X', 'X F X X', 'X F X F X', 'X XXXXX X X', 'X X X X', 'X X X X X', 'X X X X XXXX', 'X X X X', 'X X X X', 'X X X X', 'X F X X', 'X X X', 'X X F X', 'X X X X', 'X X X X', 'X X F X X', 'X F X X X', 'X X F X', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' ); const numFrutas:word = 8; (* Nuestros sprites *) type tipoSprite = array[1..ANCHOSPRITE,1..ALTOSPRITE] of byte; (* El sprite en si: matriz de 10x10 bytes *) const spriteLadrillo: tipoSprite = ((0,2,2,2,2,2,2,2,2,0), (2,1,1,1,1,1,1,1,1,2), (2,1,1,1,1,1,1,1,1,2), (2,1,1,1,1,1,1,1,1,2), (2,1,1,1,1,1,1,1,1,2), (2,1,1,1,1,1,1,1,1,2), (2,1,1,1,1,1,1,1,3,2), (2,1,1,1,1,1,1,3,3,2), (2,1,1,1,1,1,3,3,2,2), (2,2,2,2,2,2,2,2,2,0) ); const spriteComida: tipoSprite = ((0,0,0,2,0,0,0,0,0,0), (0,0,2,2,0,0,2,2,0,0), (0,4,4,4,2,2,4,4,0,0), (4,4,4,4,4,2,4,4,4,0), (4,4,4,4,4,4,4,4,4,0), (4,4,4,4,4,4,4,4,4,0), (4,4,4,4,4,4,4,4,4,0), (4,4,4,4,4,4,4,4,4,0), (4,4,4,4,4,4,4,4,4,0), (0,4,4,4,4,4,4,4,0,0) ); const spriteJugador: tipoSprite = ((0,0,3,3,3,3,3,0,0,0), (0,3,1,1,1,1,1,3,0,0), (3,1,1,1,1,1,1,1,3,0), (3,1,1,1,1,1,1,1,3,0), (3,1,1,1,1,1,1,1,3,0), (3,1,1,1,1,1,1,1,3,0), (3,1,1,1,1,1,1,1,3,0), (3,1,1,1,1,1,1,1,3,0), (0,3,1,1,1,1,1,3,0,0), (0,0,3,3,3,3,3,0,0,0) ); (* -------------- Rutina de dibujar sprites -------------- *) (* Simplemente dibuja un sprite definido anteriormente. *) (* Copia la sintaxis de "draw_sprite" de Allegro, pero es *) (* una rutina lenta, que solo usa Pascal puro, no seria *) (* adecuada en programas con muchos sprites *) procedure draw_sprite(imagen: tipoSprite; x, y: word); var i,j: word; begin for i := 1 to ANCHOSPRITE do for j := 1 to ALTOSPRITE do begin if imagen[j,i] <> 0 then putpixel(x+i-1, y+j-1, imagen[j,i]); end; end; (* -------------- Rutina de dibujar el fondo ------------- *) procedure dibujaFondo; var i, j: word; begin clearDevice; for i:= 1 to MAXCOLS do for j := 1 to MAXFILAS do begin if mapa[j,i] = 'X' then draw_sprite(spriteLadrillo, (i-1)*ESCALA, (j-1)*ESCALA); if mapa[j,i] = 'F' then draw_sprite(spriteComida, (i-1)*ESCALA, (j-1)*ESCALA); end; end; (* ------------------------------------------------ *) (* *) (* -------------- Cuerpo del programa ------------- *) var gd,gm, error : integer; BEGIN gd := D8bit; gm := m320x200; initgraph(gd, gm, ''); (* Si falla bajo Windows, probar gd:=0; gm:=0; *) (* Intentamos entrar a modo grafico *) error := graphResult; if error <> grOk then begin writeLn('No se pudo entrar a modo grafico'); writeLn('Error encontrado: '+ graphErrorMsg(error) ); halt(1); end; (* ----------------------- Si todo ha ido bien: empezamos *) dibujaFondo; (* Valores iniciales *) posX := POS_X_INI; posY := POS_Y_INI; incX := INC_X_INI; incY := INC_Y_INI; (* Parte repetitiva: *) repeat dibujaFondo; draw_sprite (spriteJugador, (posX-1)*ESCALA, (posY-1)*ESCALA); terminado := FALSE; (* Si paso por una fruta: la borro y falta una menos *) if (mapa[posY,posX] = 'F') then begin mapa[posY,posX] := ' '; numFrutas := numFrutas - 1; if (numFrutas = 0) then begin setColor(14); outTextXY( 100, 90, 'Ganaste!' ); terminado := TRUE; end; end; (* Si choco con la pared, se acabo *) if (mapa[posY,posX] = 'X') then begin setColor(13); outTextXY( 100, 90, 'Chocaste!' ); terminado := TRUE; end; if terminado then break; (* Compruebo si se ha pulsado alguna tecla *) if keypressed then begin tecla := upcase(readkey); case tecla of TEC_ARRIBA: begin incX := 0; incY := -1; end; TEC_ABAJO: begin incX := 0; incY := 1; end; TEC_IZQDA: begin incX := -1; incY := 0; end; TEC_DCHA: begin incX := 1; incY := 0; end; end; end; posX := posX + incX; posY := posY + incY; (* Pequea pausa antes de seguir *) delay ( PAUSA ); until FALSE; (* Repetimos indefininamente *) (* (la condicin de salida la comprobamos 'dentro') *) readkey; closegraph; end.
8.4. Miniserpiente 2 en Java.
Al igual que en las versiones en C y Pascal, los únicos cambios en Java se refieren a que en pantalla dibujaremos un Sprite en vez de una letra y a que debemos crear esos Sprites.
De hecho, en Java tenemos incluso un componente llamado Image (imagen), que es el equivalente directo de nuestro Sprite. La única complicación es que resulta muy fácil crear un Image a partir de un fichero de imagen, pero es bastante más difícil crearlo a partir de un "array", como hemos hecho en el caso de C y Pascal. Aun así, veamos los pasos:
Crearemos un "array" que contenga la información de los colores de cada punto:
int spriteLadrillo[] = {0,2,2,2,2,2,2,2,2,0, 2,1,1,1,1,1,1,1,1,2, 2,1,1,1,1,1,1,1,1,2, 2,1,1,1,1,1,1,1,1,2, 2,1,1,1,1,1,1,1,1,2, 2,1,1,1,1,1,1,1,3,2, 2,1,1,1,1,1,1,3,3,2, 2,1,1,1,1,1,3,3,2,2, 2,2,2,2,2,2,2,2,2,0 };
Pero en Java no existe "paleta de colores Pc estándar" ;-), así que tendremos que convertir esos colores antes de pasarlos al objeto Image. Por ejemplo, el color 1 querremos que sea azul, así que tendremos que darle el nuevo valor (255 << 24) | 255 (son 4 bytes, el primero es el canal Alpha, que deberá ser 255 para que nuestra figura sea totalmente opaca; los tres siguientes son RGB (rojo, verde y azul, en ese orden), en nuestro caso 0, 0,255. De igual modo, el color 2, que será el verde, debería ser (255 << 24) | (255 << 8), el color 4, rojo, sería (255 << 24) | (255 << 16), y el color 3, un azul claro (cyan) podría ser algo como (255 << 24) | (127 << 16) | (127 << 8) | 255.
Por tanto, para "corregir" los colores de cada punto haríamos algo como:
for(i=0; i<ANCHOSPRITE; i++) for (j=0; j<ALTOSPRITE; j++) { // El color 1 sera azul if (spriteLadrillo[i+j*ANCHOSPRITE]==1) spriteLadrillo[i+j*ANCHOSPRITE] = (255 << 24) | 255; // El color 2 sera verde if (spriteLadrillo[i+j*ANCHOSPRITE]==2) spriteLadrillo[i+j*ANCHOSPRITE] = (255 << 24) | (255 << 8); // El color 3 sera cyan if (spriteLadrillo[i+j*ANCHOSPRITE]==3) spriteLadrillo[i+j*ANCHOSPRITE] = (255 << 24) | (127 << 16) | (127 << 8) | 255; // El color 4 sera rojo if (spriteLadrillo[i+j*ANCHOSPRITE]==4) spriteLadrillo[i+j*ANCHOSPRITE] = (255 << 24) | (255 << 16);
Y ya sólo nos queda pasar toda esa información al componente "Image", que se hace usando un "MemoryImageSource", al que le indicamos el ancho y el alto y desde dónde queremos leer los datos:
ladrilloFondo = createImage(new MemoryImageSource(ANCHOSPRITE, ALTOSPRITE, spriteLadrillo, 0, ANCHOSPRITE));
Para dibujar sí que no hay complicación
if (mapa[j].charAt(i) == 'X') g.drawImage(ladrilloFondo, i*ESCALA, j*ESCALA, this);
La apariencia sería esta
Y el fuente podría ser:
/*----------------------------*/ /* Intro a la programac de */ /* juegos, por Nacho Cabanes */ /* */ /* ipj08j.java */ /* */ /* Octavo ejemplo: juego de */ /* "miniSerpiente" (aprox B) */ /* */ /* Comprobado con: */ /* - JDK 1.5.0 */ /*----------------------------*/ import java.applet.Applet; import java.awt.*; import java.awt.image.*; import java.awt.event.*; public class ipj08j extends Applet implements Runnable, KeyListener { // Posiciones X e Y iniciales final int POS_X_INI = 16; final int POS_Y_INI = 10; final int INC_X_INI = 1; final int INC_Y_INI = 0; // Pausa en milisegundos entre un "fotograma" y otro final int PAUSA = 350; // Ahora las imagenes de cada elemento Image ladrilloFondo; Image comida; Image jugador; // Escala: relacion entre tamaño de mapa y de pantalla final int ESCALA = 10; // Y el mapa que representa a la pantalla // Como usaremos modo grafico de 320x200 puntos // y una escala de 10, el tablero medira 32x20 final int MAXFILAS = 20; final int MAXCOLS = 32; String mapa[]={ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "X X X", "X F X X", "X F X F X", "X XXXXX X X", "X X X X", "X X X X X", "X X X X XXXX", "X X X X", "X X X X", "X X X X", "X F X X", "X X X", "X X F X", "X X X X", "X X X X", "X X F X X", "X F X X X", "X X F X", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }; /* Ancho y alto de los sprites */ final int ANCHOSPRITE = 10; final int ALTOSPRITE = 9; int spriteLadrillo[] = {0,2,2,2,2,2,2,2,2,0, 2,1,1,1,1,1,1,1,1,2, 2,1,1,1,1,1,1,1,1,2, 2,1,1,1,1,1,1,1,1,2, 2,1,1,1,1,1,1,1,1,2, 2,1,1,1,1,1,1,1,3,2, 2,1,1,1,1,1,1,3,3,2, 2,1,1,1,1,1,3,3,2,2, 2,2,2,2,2,2,2,2,2,0 }; int spriteComida []= {0,0,0,2,0,0,0,0,0,0, 0,0,2,2,0,0,2,2,0,0, 0,4,4,4,2,2,4,4,0,0, 4,4,4,4,4,2,4,4,4,0, 4,4,4,4,4,4,4,4,4,0, 4,4,4,4,4,4,4,4,4,0, 4,4,4,4,4,4,4,4,4,0, 4,4,4,4,4,4,4,4,4,0, 0,4,4,4,4,4,4,4,0,0 }; int spriteJugador []= {0,0,3,3,3,3,3,0,0,0, 0,3,1,1,1,1,1,3,0,0, 3,1,1,1,1,1,1,1,3,0, 3,1,1,1,1,1,1,1,3,0, 3,1,1,1,1,1,1,1,3,0, 3,1,1,1,1,1,1,1,3,0, 3,1,1,1,1,1,1,1,3,0, 0,3,1,1,1,1,1,3,0,0, 0,0,3,3,3,3,3,0,0,0 }; Thread hilo = null; // El "hilo" de la animacion int posX, posY; // Posicion actual int incX, incY; // Incremento de la posicion // Terminado: Si ha chocado o comido todas las frutas boolean terminado; int tecla; // La tecla pulsada // Y las teclas por defecto final char TEC_ARRIBA = 'e'; final char TEC_ABAJO = 'x'; final char TEC_IZQDA = 's'; final char TEC_DCHA = 'd'; int numFrutas = 8; // Rutina de crear los sprites void creaSprites() { int i, j; // Doy colores más adecuados a cada figura: ladrillos for(i=0; i<ANCHOSPRITE; i++) for (j=0; j<ALTOSPRITE; j++) { // El color 1 sera azul if (spriteLadrillo[i+j*ANCHOSPRITE]==1) spriteLadrillo[i+j*ANCHOSPRITE] = (255 << 24) | 255; // El color 2 sera verde if (spriteLadrillo[i+j*ANCHOSPRITE]==2) spriteLadrillo[i+j*ANCHOSPRITE] = (255 << 24) | (255 << 8); // El color 3 sera cyan if (spriteLadrillo[i+j*ANCHOSPRITE]==3) spriteLadrillo[i+j*ANCHOSPRITE] = (255 << 24) | (127 << 16) | (127 << 8) | 255; // El color 4 sera rojo if (spriteLadrillo[i+j*ANCHOSPRITE]==4) spriteLadrillo[i+j*ANCHOSPRITE] = (255 << 24) | (255 << 16); } // y al final los asigno ladrilloFondo = createImage(new MemoryImageSource(ANCHOSPRITE, ALTOSPRITE, spriteLadrillo, 0, ANCHOSPRITE)); // Lo mismo para la comida for(i=0; i<ANCHOSPRITE; i++) for (j=0; j<ALTOSPRITE; j++) { // El color 1 sera azul if (spriteComida[i+j*ANCHOSPRITE]==1) spriteComida[i+j*ANCHOSPRITE] = (255 << 24) | 255; // El color 2 sera verde if (spriteComida[i+j*ANCHOSPRITE]==2) spriteComida[i+j*ANCHOSPRITE] = (255 << 24) | (255 << 8); // El color 3 sera cyan if (spriteComida[i+j*ANCHOSPRITE]==3) spriteComida[i+j*ANCHOSPRITE] = (255 << 24) | (127 << 16) | (127 << 8) | 255; // El color 4 sera rojo if (spriteComida[i+j*ANCHOSPRITE]==4) spriteComida[i+j*ANCHOSPRITE] = (255 << 24) | (255 << 16); } comida = createImage(new MemoryImageSource(ANCHOSPRITE, ALTOSPRITE, spriteComida, 0, ANCHOSPRITE)); // Y lo mismo para el jugador for(i=0; i<ANCHOSPRITE; i++) for (j=0; j<ALTOSPRITE; j++) { // El color 1 sera azul if (spriteJugador[i+j*ANCHOSPRITE]==1) spriteJugador[i+j*ANCHOSPRITE] = (255 << 24) | 255; // El color 2 sera verde if (spriteJugador[i+j*ANCHOSPRITE]==2) spriteJugador[i+j*ANCHOSPRITE] = (255 << 24) | (255 << 8); // El color 3 sera cyan if (spriteJugador[i+j*ANCHOSPRITE]==3) spriteJugador[i+j*ANCHOSPRITE] = (255 << 24) | (127 << 16) | (127 << 8) | 255; // El color 4 sera rojo if (spriteJugador[i+j*ANCHOSPRITE]==4) spriteJugador[i+j*ANCHOSPRITE] = (255 << 24) | (255 << 16); } jugador = createImage(new MemoryImageSource(ANCHOSPRITE, ALTOSPRITE, spriteJugador, 0, ANCHOSPRITE)); } // Inicializacion public void init() { // Valores iniciales posX = POS_X_INI; posY = POS_Y_INI; incX = INC_X_INI; incY = INC_Y_INI; terminado = false; requestFocus(); addKeyListener(this); creaSprites(); } // Escritura en pantalla public void paint(Graphics g) { int i, j; // Primero borro el fondo en negro g.setColor( Color.black ); g.fillRect( 0, 0, 639, 479 ); // Ahora dibujo paredes y comida for(i=0; i<MAXCOLS; i++) for (j=0; j<MAXFILAS; j++) { if (mapa[j].charAt(i) == 'X') g.drawImage(ladrilloFondo, i*ESCALA, j*ESCALA, this); if (mapa[j].charAt(i) == 'F') g.drawImage(comida, i*ESCALA, j*ESCALA, this); } // Finalmente, el jugador g.drawImage(jugador, posX*ESCALA, posY*ESCALA, this); // Si no quedan frutas, se acabo g.setColor( Color.yellow ); if (numFrutas == 0) { g.drawString("Ganaste!", 100, 90); terminado = true; } // Si choco con la pared, se acabo g.setColor( Color.magenta ); if (mapa[posY].charAt(posX) == 'X') { g.drawString("Chocaste!", 100, 90); terminado = true; } if (terminado) hilo=null; } // La rutina que comienza el "Thread" public void start() { hilo = new Thread(this); hilo.start(); } // La rutina que para el "Thread" public synchronized void stop() { hilo = null; } // Y lo que hay que hacer cada cierto tiempo public void run() { Thread yo = Thread.currentThread(); while (hilo == yo) { try { Thread.sleep(PAUSA); } catch (InterruptedException e){ } posX += incX; posY += incY; // Si paso por una fruta: la borro y falta una menos if (mapa[posY].charAt(posX) == 'F') { // La borro en el mapa StringBuffer temp = new StringBuffer(mapa[posY]); temp.setCharAt(posX, ' '); mapa[posY] = temp.toString(); // y en el contador numFrutas --; } // En cualquier caso, redibujo repaint(); } } // Comprobacion de teclado public void keyTyped(KeyEvent e) { tecla=e.getKeyChar(); switch (tecla) { case TEC_ARRIBA: incX = 0; incY = -1; break; case TEC_ABAJO: incX = 0; incY = 1; break; case TEC_IZQDA: incX = -1; incY = 0; break; case TEC_DCHA: incX = 1; incY = 0; break; } repaint(); e.consume(); } public void keyReleased(KeyEvent e) { } public void keyPressed(KeyEvent e) { } }