Bienvenido(a), Visitante. Por favor, ingresa o regístrate.

Ingresar con nombre de usuario, contraseña y duración de la sesión
 

Autor Hilo: TUTORIAL 1985ALTERNATIVO SOBRE PROGRAMACION EN C DE LA GAMEBOY (Leído 52056 veces)

seiyouh

  • Humano
  • Mensajes: 24
Lo mio no es la programación pero
cuando llegues al 3º punto me veras por aqui


Davidian

  • T-7T
  • Mensajes: 450
¡Grande Lucho!

De las pruebas que hemos estado haciendo ya tenemos sonando la máquina y bastante bien por cierto.

A ver si llego a tiempo de hacer algún ejemplo para la parte del sonido ;)

pocket_lucho

  • T-500
  • Mensajes: 1 117
Muchas gracias por el tutorial,...

a la segunda vez que he tenido q escribir goto y gprintf no he podido evitarlo... vaya coñazo xDD


Citar
#define gprintf_XY(str,x,y);  gotogxy(x, y);  gprintf(str);

La semana que viene más!!

Jajajaja, es verdad, por ejemplo en vez de tanto if/else se debería hacer "gprintf( ( keys & (J_START)) ? "1" : "0");" para acortar... O incluso se podría hacer todo eso en un for o una función... pero la idea es que estos tutos sean para gente con conocimientos MINIMOS de C, el que ya controle lo verá, lo hará a su manera más elegante y se buscará la vida para programar el cacharrito... Para los próximos usaré para meter código la etiqueta CODIGO en vez de CITA por el tema de la identación y las llaves, estos primeros como eran muy sencillos los he hecho así pq se vee algo mejor para mi gusto ;)

Lo dicho, no dejéis de añadir vuestros comentarios sobre el código para que sirva de ayuda a los demás, mi tiempo es el que es y hago lo que puedo xD
última modificación: 14 Septiembre 2014, 12:38:16 por pocket_lucho

Victor

  • Humano
  • Mensajes: 14
Pues ya que lo dices... He encontrado unos cuantos ejemplos más en retroisle:
http://www.retroisle.com/others/nintendogameboy/Technical/Firmware/dev.php

También he localizado el GB Development Studio que mencionan en ese tuto, pero desde mi punto de vista no merece mucho la pena. Lo pongo por si alguien quiere darle un ojo. El BMP2GB digo yo que será útil, pero como no se que otras cosas existen, lo mismo haya algo mejor (png2gb por ejemplo)...
http://www.semis.demon.co.uk/Apps/gbdev063.zip

pocket_lucho

  • T-500
  • Mensajes: 1 117
Buenas! Acabo de llegar al foro por referencias, muy interesante todo.
Me encanta ver que la gente se tira a divulgar y a hacer cosas sobre la pequeña de nintendo  :D
Comentar que si alguien se queda con ganas de más y tirarse a lo "hardcore", podeis echar un vistazo a mi tutorial de ensamblador para gameboy:
http://wiki.ladecadence.net/doku.php?id=tutorial_de_ensamblador
Aunque no querais meteros con ASM, tiene bastante documentación sobre las interioridades de la gameboy, puede seros de ayuda.
Un saludo, nos veremos por aqui  :)

Muy buenos tutoriales en ensamblador si señor! Y todo muy bien explicado, si señor ;) Ya se donde ir cuando busque optimizar jejeje

pocket_lucho

  • T-500
  • Mensajes: 1 117
Pues ya que lo dices... He encontrado unos cuantos ejemplos más en retroisle:
http://www.retroisle.com/others/nintendogameboy/Technical/Firmware/dev.php

También he localizado el GB Development Studio que mencionan en ese tuto, pero desde mi punto de vista no merece mucho la pena. Lo pongo por si alguien quiere darle un ojo. El BMP2GB digo yo que será útil, pero como no se que otras cosas existen, lo mismo haya algo mejor (png2gb por ejemplo)...
http://www.semis.demon.co.uk/Apps/gbdev063.zip

Pues muchas gracias, hay tutos ahi tb interesantes! Yo como conversor para imagenes uso este online http://www.chrisantonellis.com/gameboy/gbtdg/, para crear tiles el GBTD y para mapas el GBMB, pero vamos, que los iremos viendo en los proximos tutos ;)

Bubu

  • T-600
  • Mensajes: 2 598
Grandioso, pocket_lucho. Esto me recuerda años ha, cuando chiquetito, que me dio por pogramar en ensamblador en la GB color, jiji. Me resultó sencillo porque al ser un Z80 tenía el mismo micro que el Spectrum ;-) ¡¡pero con sprites!!


Si algo funciona...  ¡¡No lo toques!!
¡¡Pero ni de coña!!

Victor

  • Humano
  • Mensajes: 14
Tetris para GB en GBDK y C. Fuentes y Compilado. También hay un tuto explicando uno de los ejemplos que vienenn de series en el GBDK:
http://forums.tigsource.com/index.php?topic=10848.msg345959#msg345959

Hay fuentes de un Scroller también, pero está en assembler.

pocket_lucho

  • T-500
  • Mensajes: 1 117
TUTORIAL 3: SPRITES

Para la Game Boy, un sprite es una imagen de 8x8 u 8x16 pixels que se puede mover por la pantalla de forma independiente al fondo y a otros sprites. La consola presenta ciertas limitaciones además del tamaño, solo puede haber 40 sprites en pantalla y como mucho 10 en la misma linea horizontal (scanline) o se producirá parpadeo (flicker). Además, dentro de que la consola solo trabaja con 4 colores (Blanco, Negro, Gris claro y Gris oscuro), se puede usar 2 paletas de 4 colores para los sprites (siendo el primero el transparente). Para la Game Boy COLOR, estás limitaciones varían ligeramente, por ejemplo, poseemos 8 paletas de 4 colores para los sprites.

Ahora que ya sabemos esto, podemos pasar a dibujar los tiles que formaran nuestros sprites (imagenes de 8x8 pixels). Un sprite de 8x8 pixels usará un tile y otro de 8x16 pues usará dos. Para crear nuestros tiles usaremos el programa de Harry Mulder incluido en la carpeta TOOLS llamado GBTD (Game Boy Tile Designer). El programa es muy sencillo de usar, contiene las típicas herramientas de lápiz, cubo de pintura, espejados, etc.



De todas formas, siempre podéis dibujar vuestros tiles en cualquier programa de dibujo, copiar en el portapapeles y pegarlos en el GBTD. Siempre que respetéis esta paleta que os enseño no habrá problema al importar.



Cuando tengamos nuestros tiles listos, deberemos exportarlos. Para ello, vamos al menu "FILE->EXPORT TO". En "TYPE" elegimos "GBDK C FILE" y en en "FILENAME", pues la ruta donde vayamos a escribir nuestro código, por ejemplo "C:\gbdk\examples\03_sprites_A". La sección de "SETTINGS" debereis elegir el nombre al que le vamos a poner al vector de salida, la cantidad de tiles a guardar (en mi caso de 0 a 3), el formato de "GAME BOY 4 COLOR" y que se exporten los tiles como una unidad.



Si abrimos el fichero "export.c" creado, podremos ver el resultado de nuestros tiles convertidos:

Código: [Seleccionar]
unsigned char tile_data[] =
{
  0xFF,0xFF,0x81,0xFF,0x8D,0xF3,0xBD,0xC3,
  0x8D,0xF3,0x8D,0xF3,0x81,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xBD,0xC3,0x8D,0xF3,
  0xB1,0xCF,0xBD,0xC3,0x81,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xBD,0xC3,0x8D,0xF3,
  0x9D,0xE3,0x8D,0xF3,0xBD,0xC3,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xAD,0xD3,0xBD,0xC3,
  0x8D,0xF3,0x8D,0xF3,0x81,0xFF,0xFF,0xFF
};

Recordad siempre añadir "CONST" delante de todo para asegurarnos el compilador mete este vector en ROM y no en RAM.

Una vez que ya tenemos los tiles, podemos empezar con nuestro "main.c", en mi caso, en la carpeta "C:\gbdk\examples\03_sprites_A"

Código: [Seleccionar]
#include <gb/gb.h>

// los tiles que usaran los sprites
const unsigned char tile_data[] =
{
  0xFF,0xFF,0x81,0xFF,0x8D,0xF3,0xBD,0xC3,
  0x8D,0xF3,0x8D,0xF3,0x81,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xBD,0xC3,0x8D,0xF3,
  0xB1,0xCF,0xBD,0xC3,0x81,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xBD,0xC3,0x8D,0xF3,
  0x9D,0xE3,0x8D,0xF3,0xBD,0xC3,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xAD,0xD3,0xBD,0xC3,
  0x8D,0xF3,0x8D,0xF3,0x81,0xFF,0xFF,0xFF
};

//////////////////////////////////////////////////////
// punto de entrada
//////////////////////////////////////////////////////
void main(){

    // modo de sprites a 8x8 pixels
    SPRITES_8x8;

Elegimos el tamaño de sprites de 8x8, no se pueden mezclar, todos deben ser del mismo tamaño (8x8 u 8x16).

Citar
    // carga los tiles de los sprites, posicion, cantidad de tiles y los tiles
    set_sprite_data( 0, 4, tile_data);

Cargamos en VRAM los tiles de los sprites especificando posición en memoria, la cantidad de tiles y el vector de los tiles. Podemos almacenar hasta 256 tiles para los sprites, hay que tener en cuenta que la zona de memoria de 128 a 255 es compartida con la memoria del fondo, lo veremos en detalle cuando lleguemos a estos.

Citar
    // asigna a cada sprite un tile, sprite (0-39), posicion del tile
    set_sprite_tile( 0, 0 );
    set_sprite_tile( 1, 1 );
    set_sprite_tile( 2, 2 );
    set_sprite_tile( 3, 3 );

Asignamos a un sprite un tile, especificando el número de sprite (0-39) y la posición en VRAM del tile (0-255)

Citar
    // mueve el sprite a la posicion x, y
    move_sprite( 0, 20, 20 );
    move_sprite( 1, 28, 20 );
    move_sprite( 2, 36, 20 );
    move_sprite( 3, 44, 20 );

Movemos los sprites a una posición de la pantalla, especificando el número de sprite (0-39) y la coordenada X/Y (la pantalla de la consola es de 160x144 pixels).

Citar
    // muestra los sprites
    SHOW_SPRITES;

    while(1)    ;
}

Por último mostramos los sprites y entramos en un bucle infinito. El código completo quedaría así:

Código: [Seleccionar]
#include <gb/gb.h>

const unsigned char tile_data[] =
{
  0xFF,0xFF,0x81,0xFF,0x8D,0xF3,0xBD,0xC3,
  0x8D,0xF3,0x8D,0xF3,0x81,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xBD,0xC3,0x8D,0xF3,
  0xB1,0xCF,0xBD,0xC3,0x81,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xBD,0xC3,0x8D,0xF3,
  0x9D,0xE3,0x8D,0xF3,0xBD,0xC3,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xAD,0xD3,0xBD,0xC3,
  0x8D,0xF3,0x8D,0xF3,0x81,0xFF,0xFF,0xFF
};


//////////////////////////////////////////////////////
// punto de entrada
//////////////////////////////////////////////////////
void main(){

    // modo de sprites a 8x8 pixels
    SPRITES_8x8;

    // carga los tiles de los sprites, posicion, cantidad de tiles y los tiles
    set_sprite_data( 0, 4, tile_data);

    // asigna a cada sprite un tile, sprite (0-39), posicion del tile
    set_sprite_tile( 0, 0);
    set_sprite_tile( 1, 1);
    set_sprite_tile( 2, 2);
    set_sprite_tile( 3, 3);

    // mueve el sprite a la posicion x, y
    move_sprite( 0, 20, 20 );
    move_sprite( 1, 28, 20 );
    move_sprite( 2, 36, 20 );
    move_sprite( 3, 44, 20 );

    // muestra los sprites
    SHOW_SPRITES;

    while(1)    ;
}


Ya podríamos crear el "compile.bat" con el mismo texto de hasta ahora y obtener el siguiente resultado:

Citar
lcc -o rom.gb main.c
pause
bgb.exe rom.gb



COLOR

El siguiente paso que vamos a abarcar es darle color a nuestros sprites.

Primero añadiremos las paletas que vamos a utilizar, recordad que podemos cargar hasta 8 para los sprites.

Código: [Seleccionar]
const UWORD sprite_palette[] = {
RGB_RED, RGB_DARKRED, RGB_GREEN, RGB_DARKGREEN,
RGB_BLUE, RGB_DARKBLUE, RGB_YELLOW, RGB_DARKYELLOW,
RGB_CYAN, RGB_AQUA, RGB_PINK, RGB_PURPLE,
RGB_LIGHTGRAY, RGB_DARKGRAY, RGB_WHITE, RGB_LIGHTFLESH,
RGB_BLACK, RGB_BROWN, RGB_ORANGE, RGB_TEAL
};

En "include\cgb.h" tenéis la siguiente definición de los colores basados en la paleta por defecto EGA:

#define RGB_RED        RGB(31,  0,  0)
#define RGB_DARKRED    RGB(15,  0,  0)
#define RGB_GREEN      RGB( 0, 31,  0)
#define RGB_DARKGREEN  RGB( 0, 15,  0)
#define RGB_BLUE       RGB( 0,  0, 31)
#define RGB_DARKBLUE   RGB( 0,  0, 15)
#define RGB_YELLOW     RGB(31, 31,  0)
#define RGB_DARKYELLOW RGB(21, 21,  0)
#define RGB_CYAN       RGB( 0, 31, 31)
#define RGB_AQUA       RGB(28,  5, 22)
#define RGB_PINK       RGB(11,  0, 31)
#define RGB_PURPLE     RGB(21,  0, 21)
#define RGB_BLACK      RGB( 0,  0,  0)
#define RGB_DARKGRAY   RGB(10, 10, 10)
#define RGB_LIGHTGRAY  RGB(21, 21, 21)
#define RGB_WHITE      RGB(31, 31, 31)

#define RGB_LIGHTFLESH RGB(30, 20, 15)
#define RGB_BROWN      RGB(10, 10,  0)
#define RGB_ORANGE     RGB(30, 20,  0)
#define RGB_TEAL       RGB(15, 15,  0)

Ya dentro de nuestro "main.c" colocamos el código siguiente:

Código: [Seleccionar]
    if( _cpu == CGB_TYPE ){

         // carga las paletas de los sprites, paleta inicial, numero de paletas a cargar y datos
        set_sprite_palette( 0, 4, &sprite_palette[0] );

        //  carga la paleta de cada sprite (numero de sprite, numero de paleta)
        set_sprite_prop( 0, 0 );
        set_sprite_prop( 1, 1 );
        set_sprite_prop( 2, 2 );
        set_sprite_prop( 3, 3 );
    }

Si detectamos que estamos en una Game Boy COLOR (CGB_TYPE), cargamos las paletas de los sprites y luego asignamos a cada sprite una paleta. Es recomendable hacer la comprobación para que nuestra rom sea 100% compatible con la Game Boy clásica. Con "set_sprite_prop" no solo podemos elegir la paleta, tambien podemos cambiar otras propiedades de los sprites como su espejado (flip) usando S_FLIPX (bit 6 a 1 -> 00100000 o 0x20U) o S_FLIPY (bit 7 a 1 -> 01000000 o 0x40U), su prioridad respecto al fondo con S_PRIORITY (bit 8 a 1 -> 10000000 o 0x80U) o en modo monocromo elegir entre las dos posibles paletas para los sprites, OBJ1PAL o OBJ0PAL. Hablando de paletas, por defecto, GBDK carga en modo Game Boy Clásica las paletas con la secuencia BLANCO, GRIS CLARO, GRIS OSCURO y NEGRO en las 3 paletas, podemos cambiar este orden haciendo:

Citar
BGP_REG = OBP0_REG = OBP1_REG = 0xE4U; // 0xE4U seria la normal -> 11100100, 0xFFU seria todo oscuro -> 11111111, etc.

Usando las opciones de debug de los emuladores incluidos podremos verlas.



El código completo quedaría de la siguiente manera:

Código: [Seleccionar]
#include <gb/gb.h>

const unsigned char tile_data[] =
{
  0xFF,0xFF,0x81,0xFF,0x8D,0xF3,0xBD,0xC3,
  0x8D,0xF3,0x8D,0xF3,0x81,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xBD,0xC3,0x8D,0xF3,
  0xB1,0xCF,0xBD,0xC3,0x81,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xBD,0xC3,0x8D,0xF3,
  0x9D,0xE3,0x8D,0xF3,0xBD,0xC3,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xAD,0xD3,0xBD,0xC3,
  0x8D,0xF3,0x8D,0xF3,0x81,0xFF,0xFF,0xFF
};


const UWORD sprite_palette[] = {
RGB_RED, RGB_DARKRED, RGB_GREEN, RGB_DARKGREEN,
RGB_BLUE, RGB_DARKBLUE, RGB_YELLOW, RGB_DARKYELLOW,
RGB_CYAN, RGB_AQUA, RGB_PINK, RGB_PURPLE,
RGB_LIGHTGRAY, RGB_DARKGRAY, RGB_WHITE, RGB_LIGHTFLESH,
RGB_BLACK, RGB_BROWN, RGB_ORANGE, RGB_TEAL
};


//////////////////////////////////////////////////////
// punto de entrada
//////////////////////////////////////////////////////
void main(){

    // modo de sprites a 8x8 pixels
    SPRITES_8x8;

    // carga los tiles de los sprites, posicion, cantidad de tiles y los tiles
    set_sprite_data( 0, 4, tile_data);

    // asigna a cada sprite un tile, sprite (0-39), posicion del tile
    set_sprite_tile( 0, 0);
    set_sprite_tile( 1, 1);
    set_sprite_tile( 2, 2);
    set_sprite_tile( 3, 3);

    if( _cpu == CGB_TYPE ){

         // carga las paletas de los sprites, paleta inicial, numero y datos
        set_sprite_palette( 0, 4, &sprite_palette[0] );

        //  carga la paleta de cada sprite (numero de sprite, numero de paleta)
        set_sprite_prop( 0, 0 );
        set_sprite_prop( 1, 1 );
        set_sprite_prop( 2, 2 );
        set_sprite_prop( 3, 3 );
    }

    // mueve el sprite a la posicion x, y
    move_sprite( 0, 20, 20 );
    move_sprite( 1, 28, 20 );
    move_sprite( 2, 36, 20 );
    move_sprite( 3, 44, 20 );

    // muestra los sprites
    SHOW_SPRITES;

    while(1)    ;
}

Por último debemos hacer un cambio más, en nuestro "compile.bat" debemos modificar la linea de comandos del "lcc".

Citar
lcc -Wl-yp0x143=0x80 -o rom.gb main.c
pause
bgb.exe rom.gb

Con esto hemos dado valor a 0x143 para que sea CGB-compatible (0x80). Ya podemos compilar la rom y obtener el siguiente resultado:




BANCOS

Como comenta xzakox en su tutorial de ensamblador: "la memoria principal de la gameboy, mapeada en un espacio de 16 bit, nos permite direccionar directamente 64K (2^16 = 65536). En este espacio de direcciones, tenemos que direccionar todos los bloques de memoria a los que la gameboy necesita acceder, esto es, la RAM, la ROM del cartucho, la RAM interna del cartucho para los juegos que graban partidas, la memoria de vídeo, etc. Para la ello los diseñadores de la GameBoy mapearon la memoria en diferentes bloques necesarios, como la RAM interna o la memoria de video, dejando dos bloques de 16K para el acceso a la ROM de los juegos, y un bloque de 8K para el acceso a la RAM de los juegos (partidas guardadas). Como muchos juegos empezaron a requerir mas de 32K de ROM o de 8K de RAM de guardado, se empezó a emplear una técnica denominada “Banking”, en la que la ROM del juego se divide en diversos bloques que se puedan independizar (los gráficos o sonidos de cada pantalla por ejemplo), que se van mapeando en el bloque de acceso a la memoria según sean necesarios. En la GameBoy, esto se diseñó de la siguiente manera; tenemos un bloque fijo de 16K(donde programamos la lógica principal del juego), y luego, mediante ciertas instrucciones (dependiendo del chip de mapping que usemos en nuestro cartucho), podemos ir intercambiando bancos de 16K en el otro bloque disponible."

Debido a esto, es necesario pensar en ir guardando todo lo que no sea la lógica del juego, en bancos de memoria diferentes al primero, para ello usaremos el mapper MBC1 soportado por las GBDK. En realidad no es tan complicado como suena.

Si recordais al principio cuando exportamos los tiles que dibujamos en GBTD, se nos creó un fichero llamado "export.c" que es donde estaba el vector de los tiles que copiamos en nuestro main. Lo que vamos a hacer es renombrarlo a "tiles.c" y compilarlo aparte del "main.c" para a la hora de generar la rom, poder elegir en que banco de memoria guardarlo.

Simplemente debemos modificar en "main.c" donde copiamos el contenido del vector de esto:

Código: [Seleccionar]
const unsigned char tile_data[] =
{
  0xFF,0xFF,0x81,0xFF,0x8D,0xF3,0xBD,0xC3,
  0x8D,0xF3,0x8D,0xF3,0x81,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xBD,0xC3,0x8D,0xF3,
  0xB1,0xCF,0xBD,0xC3,0x81,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xBD,0xC3,0x8D,0xF3,
  0x9D,0xE3,0x8D,0xF3,0xBD,0xC3,0xFF,0xFF,
  0xFF,0xFF,0x81,0xFF,0xAD,0xD3,0xBD,0xC3,
  0x8D,0xF3,0x8D,0xF3,0x81,0xFF,0xFF,0xFF
};

A esto:

Código: [Seleccionar]
extern const unsigned char tile_data[];

Y que justo antes de cargar estos tiles en VRAM, debemos especificar en que banco hemos de acceder con:

Código: [Seleccionar]
// saltamos al banco de memoria 2
SWITCH_ROM_MBC1(2);

set_sprite_data( 0, 4, tile_data );

El fichero "compile.bat" si que sufrirá más cambios .

Citar
lcc -c -o main.o main.c

Ahora solo creamos el objeto de main.c

Citar
lcc -Wf-bo2 -c -o  tiles.o tiles.c

Creamos el objeto de tiles.c especificando en que banco se colocará con -Wf-boX (en este caso el banco 2)

Citar
lcc -Wl-yp0x143=0x80 -Wl-yt1 -Wl-yo4 -o rom.gb main.o tiles.o

Y ya podemos crear nuestra rom, con -Wl-ytX especificamos que será una ROM con MBC1, los distintos tipos posibles son:

0 : ROM ONLY
1 : ROM+MBC1
2 : ROM+MBC1+RAM
3 : ROM+MBC1+RAM+BATTERY
5 : ROM+MBC2
6 : ROM+MBC2+BATTERY


Con -Wl-yoX especificamos el numero de bancos que tendrá nuestra ROM, en este caso, 4. Podemos poner hasta 32 (512 KB) con el mapper MBC1, siendo el primer banco el 1, así que empezar a meter vuestros datos en el dos.

Citar
pause
bgb.exe rom.gb

Acostumbraos a usar "Banking", no es difícil y muchos seguro que ya lo conoceis si programais para ordenadores de 8 bits, máquinas posteriores como Mega Drive pueden direccionar 32 megabits sin banking (4 megabytes), cosa que se agradece pero en cambio otras supuestamente más poderosas como Super Nintendo hay que volver a utilizarlo así que no está de más practicar.
última modificación: 19 Septiembre 2014, 17:29:46 por pocket_lucho

pocket_lucho

  • T-500
  • Mensajes: 1 117

METASPRITES

Algunos ya habreis cabilado que eso de trabajar con sprites de 8x8 o como mucho de 8x16 es una puñeta si quieres hacer personajes grandes y vistosos... lo suyo es acudir a metasprites. ¿Y qué es un metasprite? Pues "simplemente" un sprite formado por múltiples sprites. Para ello, vamos a recurrir a unas rutinas que el señor Shen Mansell escribió para facilitarnos la vida http://shen.mansell.tripod.com/games/gameboy/gameboy.html.

No voy a explicar línea a línea como están escritas estas rutinas, pero si os voy a enseñar un ejemplo de como usarlas. Por ello, os he subido este fichero con todo lo necesario dentro https://dl.dropboxusercontent.com/u/33369593/tutosGB/03_B_metasprites.rar (rutinas, gráficos...).

Código: [Seleccionar]
#include <gb/gb.h>

// rutinas de manejo de metasprites (sprite formado por varios sprites)
#include "lib_support/char_utils.h"

Enlazamos con las rutinas de Shen para el uso de metasprites.

Código: [Seleccionar]
// tamaño del sprite
#define SPRITE_00_W 3
#define SPRITE_00_H 4

#define SPRITE_00_S SPRITE_00_W*SPRITE_00_H

Definimos el tamaño en tiles de nuestro metasprite, 3x4 tiles en nuestro caso.

Código: [Seleccionar]
// tiles de los sprites
extern unsigned char sprite01_tiledata[];
extern unsigned char sprite02_tiledata[];
extern unsigned char sprite03_tiledata[];
extern unsigned char sprite04_tiledata[];
extern unsigned char sprite05_tiledata[];
extern unsigned char sprite06_tiledata[];

Enlazamos los tiles de los distintos frames que tendrá nuestro metasprite.

Código: [Seleccionar]
// estructura basica de un personaje, id, posicion, grafico
struct PG{
UBYTE id;
UBYTE x;
UBYTE y;
UBYTE graphic;
};

// jugador
struct PG player;

// metasprite del jugador
UBYTE sprPlayer;

Nos creamos un estructura para un "jugador" y variables minimas de control para su metasprite.

Código: [Seleccionar]
//////////////////////////////////////////////////////
//
//  void playerMoving( UBYTE keys )
//  mueve el metasprite del jugador
//
//////////////////////////////////////////////////////
void playerMoving( UBYTE keys ){

if( keys & J_DOWN )
if( player.y != 104 )
            ++player.y; // 112 = 144 - (altura sprite * 8)

if( keys & J_UP )
if( player.y != 0 )
            --player.y;

if( keys & J_LEFT )
if( player.x != 0 )
            --player.x;

if( keys & J_RIGHT )
if( player.x != 136 )
            ++player.x; // 136 = 144 - (ancho sprite * 8)


    moveSprite( player.id, player.x, player.y, SPRITE_00_W, SPRITE_00_H);

    return;
}

Esta función llamada desde "main()" recibirá las teclas pulsadas y llamará a las rutinas de movimiento del metasprite, modificando primero la estructura del "jugador".

Código: [Seleccionar]
//////////////////////////////////////////////////////
//
//  void updatePlayerAnimation(){
//  cambia la animacion del meta sprite del jugador
//
//////////////////////////////////////////////////////
void updatePlayerAnimation(){

    static UBYTE _timer2 = 0;
_timer2++;

if( _timer2 == 61)
        _timer2 = 0;

if( _timer2 == 0)
changeSprGraphics( 0, sprite01_tiledata, SPRITE_00_S );
else if( _timer2 == 10 )
changeSprGraphics( 0, sprite02_tiledata, SPRITE_00_S );
else if( _timer2 == 20 )
changeSprGraphics( 0, sprite03_tiledata, SPRITE_00_S );
else if( _timer2 == 30 )
changeSprGraphics( 0, sprite04_tiledata, SPRITE_00_S );
else if( _timer2 == 40 )
changeSprGraphics( 0, sprite05_tiledata, SPRITE_00_S );
else if( _timer2 == 50 )
changeSprGraphics( 0, sprite06_tiledata, SPRITE_00_S );
}

Esta función llamada desde "main()" simplemente animará nuestro metasprite.

Código: [Seleccionar]
//////////////////////////////////////////////////////
//
//  void main(){
//  punto de entrada
//
//////////////////////////////////////////////////////
void main(){

    UBYTE keys;

    // desactiva la pantalla
    DISPLAY_OFF;
    disable_interrupts();

    // sprites de 8x8
    SPRITES_8x8;

    // esconde todo
    HIDE_SPRITES;
    HIDE_BKG;
    HIDE_WIN;

    // carga del sprite del personaje
    sprPlayer = loadSpriteGraphics( sprite01_tiledata, SPRITE_00_S );
    player.id = setupSprite( sprPlayer, SPRITE_00_W, SPRITE_00_H );
    player.x = 10;
    player.y = 72;
    moveSprite( player.id, player.x, player.y, SPRITE_00_W, SPRITE_00_H);

Ya en "main()", cargamos los tiles de metasprite indicando sus tiles y tamaño con "loadSpriteGraphics" recibiendo su metasprite que usaremos después con "setupSprite" para darle valores, recibiendo su id necesario para posteriormente moverlo con "moveSprite". La parte de desactivación de la pantalla es para evitar basura durante el proceso hasta que finalice, es recomendado hacerlo cuando vayamos a hacer grandes cambios en la pantalla.

Código: [Seleccionar]
    SHOW_BKG;
    //SHOW_WIN;
    SHOW_SPRITES;
    DISPLAY_ON;
    enable_interrupts();

    // bucle infinito
    while(1) {
// sincroniza la pantalla
wait_vbl_done();

// lee el pad
keys = joypad();

        // mueve el jugador
        playerMoving( keys );

        //  cambia la animacion del meta sprite del jugador
updatePlayerAnimation();
    }
}

Ya dentro del bucle infinito, simplemente vamos controlando las pulsaciones de los botones y llamamos a las funciones de movimiento y animación del metasprite. Recordad que como acceden a la VRAM, hay que hacerlo DESPUÉS del blanqueo con "wait_vbl_done();"

El código completo de "main.c" quedaría así:

Código: [Seleccionar]
#include <gb/gb.h>

// rutinas de manejo de metasprites (sprite formado por varios sprites)
#include "lib_support/char_utils.h"

// tamaño del sprite
#define SPRITE_00_W 3
#define SPRITE_00_H 4

#define SPRITE_00_S SPRITE_00_W*SPRITE_00_H

// tiles de los sprites
extern unsigned char sprite01_tiledata[];
extern unsigned char sprite02_tiledata[];
extern unsigned char sprite03_tiledata[];
extern unsigned char sprite04_tiledata[];
extern unsigned char sprite05_tiledata[];
extern unsigned char sprite06_tiledata[];

// estructra basica de un personaje, id, posicion, grafico
struct PG{
UBYTE id;
UBYTE x;
UBYTE y;
UBYTE graphic;
};

// jugador
struct PG player;

// sprite del jugador
UBYTE sprPlayer;

//////////////////////////////////////////////////////
//
//  void playerMoving( UBYTE keys )
//  mueve el meta sprite del jugador
//
//////////////////////////////////////////////////////
void playerMoving( UBYTE keys ){

    if( keys & J_DOWN )
        if( player.y != 104 )
            ++player.y; // 112 = 144 - (altura sprite * 8)

    if( keys & J_UP )
if( player.y != 0 )
            --player.y;

    if( keys & J_LEFT )
if( player.x != 0 )
            --player.x;

    if( keys & J_RIGHT )
if( player.x != 136 )
            ++player.x; // 136 = 144 - (ancho sprite * 8)

    moveSprite( player.id, player.x, player.y, SPRITE_00_W, SPRITE_00_H);

    return;
}


//////////////////////////////////////////////////////
//
//  void updatePlayerAnimation(){
//  cambia la animacion del meta sprite del jugador
//
//////////////////////////////////////////////////////
void updatePlayerAnimation(){

    static UBYTE _timer2 = 0;
_timer2++;

    if( _timer2 == 61)
        _timer2 = 0;

if( _timer2 == 0)
changeSprGraphics( 0, sprite01_tiledata, SPRITE_00_S );
else if( _timer2 == 10 )
changeSprGraphics( 0, sprite02_tiledata, SPRITE_00_S );
else if( _timer2 == 20 )
changeSprGraphics( 0, sprite03_tiledata, SPRITE_00_S );
else if( _timer2 == 30 )
changeSprGraphics( 0, sprite04_tiledata, SPRITE_00_S );
else if( _timer2 == 40 )
changeSprGraphics( 0, sprite05_tiledata, SPRITE_00_S );
else if( _timer2 == 50 )
changeSprGraphics( 0, sprite06_tiledata, SPRITE_00_S );
}


//////////////////////////////////////////////////////
//
//  void main(){
//  punto de entrada
//
//////////////////////////////////////////////////////
void main(){

    UBYTE keys;

    // desactiva la pantalla
    DISPLAY_OFF;
    disable_interrupts();

    // sprites de 8x8
    SPRITES_8x8;

    // esconde todo
    HIDE_SPRITES;
    HIDE_BKG;
    HIDE_WIN;

    // carga del sprite del personaje
    sprPlayer = loadSpriteGraphics( sprite01_tiledata, SPRITE_00_S );
    player.id = setupSprite( sprPlayer, SPRITE_00_W, SPRITE_00_H );
    player.x = 10;
    player.y = 72;
    moveSprite( player.id, player.x, player.y, SPRITE_00_W, SPRITE_00_H);

    SHOW_BKG;
    //SHOW_WIN;
    SHOW_SPRITES;
    DISPLAY_ON;
    enable_interrupts();

    // bucle infinito
    while(1) {
// sincroniza la pantalla
wait_vbl_done();

// lee el pad
keys = joypad();

        // mueve el jugador
        playerMoving( keys );

        //  cambia la animacion del meta sprite del jugador
        updatePlayerAnimation();
    }
}

Un posible "compile.bat" sencillo sería este:

Citar
lcc -c -o  sprite_data.o pics/sprite_data.c
lcc -c -o char_utils.o lib_support/char_utils.c
lcc -c -o main.o main.c
lcc -o rom.gb main.o char_utils.o sprite_data.o
pause
bgb.exe rom.gb

Y ya tenemos nuestro metasprite de 3x8 tiles moviéndose de forma sencilla.




DEBERES
Esta lección creo que ha quedado muy completita asi que seguro que estais deseosos de hacer múltiples pruebas. Aparte de las típicas, os recomendaría añadir al ejemplo de metasprites soporte de color, banking, flip y soporte para sprites de 8x16, a ver quien es el primero en hacerlo ;)

kalzakath

  • Visitante
Olé tú. Una versión em pdf para la página principal de fasebonus para cuándo?

Balthier

  • T-70
  • Mensajes: 100
A ver wi lo del Banking me ha quedado claro. Básicamente tenemos varios bloques de memoria en los cuales almacenamos datos como sprites o sonidos y los vamos intercambiando según  los vamos precisando no?

Es como si tuviera un mueble con varios cajones. En un cajón tenemos calcetines, en otro calzones, en otro bragas, etc... Y cuando queremos acceder a ese contenido lo hacemos mediante la intrucción precisa?
"...7 secrets, 7 sins, this is where the end begins..."


karkayu

  • T-1
  • Mensajes: 50
  • Autentico y genuino 'Luser'
    • PNOID
PNOID para ZX Spectrum: http://pnoid.jaimeweb.es/

Victor

  • Humano
  • Mensajes: 14
Completito, completito. Lo de los metaSprites me ha encantado, que te lo había pedido ya  ^-^

Una duda tras una lectura rápida (seguro que luego vienen más).
¿Porque declaras 4 bancos si realmente solo usas 2?
¿Los bancos se numeran a partir del 1 o el 0? Porque estás usando el 2 para los tiles, imagino que en el 1 está el main y en el 0 y el 3 (o en el 3 y el 4) ¿no hay nada?

Esta noche intentaré darle caña, aunque sea con una mano :P