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 programacion Megadrive - SGDK (Leído 9411 veces)

pocket_lucho

  • T-500
  • Mensajes: 1 117
Lo prometido es deuda asi que con la ayuda del usuario realbrucest vamos a ir haciendo unos tutoriales de las librerías SGDK para hacer vuestras cosillas en el lenguaje C para Megadrive.

Con este lenguaje ganaremos en versatilidad y sobre todo, velocidad, respecto al uso de Basic aunque evidentemente es necesario tener nociones de C que es más complicado que Basic. Lo primero es lo primero así que empezaremos con la instalación del entorno bajo la ide Code:Blocks.

TUTORIAL 1: INSTALACIÓN DEL ENTORNO - HOLA MUNDO

Este mismo tutorial lo tenéis en la wiki:

http://www.elotrolado.net/wiki/Uso_de_SGDK_en_CodeBlocks

Básicamente, realbrucest ha traducido el original de la web de sgdk aportando poco más de cosecha propia. Cualquier comentario o sugerencia o lo que sea será bien recibido.

A - Descarga e instalación de Code:Blocks

Code:Blocks es un entorno de desarrollo integrado para trabajar en lenguaje C++, libre y multiplataforma.

En la sección de descargas de la página oficial bajaremos la versión más reciente de éste correspondiente a nuestro sistema operativo.

http://www.codeblocks.org/

Luego se procederá a su instalación en la ubicación que se desee.




B - Descarga e instalación de SGDK
Apartado de descargas en la web de SGDK:

http://code.google.com/p/sgdk/downloads/list

Para proceder a su instalación no hay más que descomprimir la carpeta en la ruta que se quiera. (En este ejemplo se ubicará en C:\\ con el nombre sgdk, lo que vendría a ser C:\\sgdk)


C - Creación de las variables de entorno

Antes de abrir siquiera Code::Blocks, lo siguiente que se precisa es crear dos variables de entorno. Para lo cual hemos de acceder a la ventana de \"Propiedades del sistema\" y una vez ahí nos encontraremos el botón \"Variables de entorno\" en la pestaña de \"Opciones avanzadas\".

Crearemos las siguientes variables:
GDK: dándole como valor la ruta donde hayamos guardado la carpeta del sgdk usando nomenclatura Unix.
Ej: c:/sgdk

GDK_WIN: dándole como valor la ruta donde hayamos guardado la carpeta del sgdk usando nomenclatura Unix.
Ej: c:\\sgdk




D- Configuración del compilador

Ahora ya sí hemos de ejecutar Code::Blocks. Con éste abierto, seleccionaremos la Settings en la barra de tareas y en el menú desplegable Compiler and debugger...



Crearemos una nueva configuración de compilador a partir de una copia del cómpilador GNU GCC básico (GNU GCC Compiler). Le daremos el nombre que nos apetezca (\"Sega Genesis Compiler\" en el presente tutorial).



Accederemos ahora a la pestaña \"Toolchains executables\" donde primeramente, en el cuadro \"Compiler\'s installation directory\" indicaremos la ruta en donde hemos guardado el sgdk. El resto de cuadros han de contener la siguiente información:
C compiler: gcc.exe
C++ compiler: gcc.exe
Linker for dynamics libs: gcc.exe
Linker for dynamics libs: ld.exe
Debugger: gdb.exe
Resource compiler: bintoc.exe
Make program: make.exe



E - Creación de un proyecto de prueba

Llegados a este punto ya tenemos Code:Blocks configurado para poder realizar nuestros programas para Megadrive. Los siguientes pasos nos van a ayudar a crear un proyecto básico para que nos aseguremos del correcto funcionamiento de todo lo realizado hasta ahora.

Crea un nuevo proyecto vacío.



Especifica el nombre que deseas que tenga y la ruta donde ha de guardarse.



Selecciona el compilador que configuramos previamente (\"Sega Genesis Compiler\" en el tutorial).
Desmarca la casilla Create \"Debug\" configuration.
Renombra \"Release\" configuration a \"default\".
Cambia los directorios de salida por \"out\\\".
Haz click en Finish.



F - Propiedades del Proyecto
Se requiere modificar algunos valores en las propiedadeds del proyecto.

En el menú contextual vinculado al nuevo proyecto seleciona Properties...



Hemos de indicar que vamos a utilizar el makefile incluido en el sgdk. Por tanto, especificaremos la ruta completa de este archivo y marcaremos el checkbox \"This is a custom Makefile\".
Puede que sea necesario salir y volver a la ficha de propiedades antes de poder ver la ventana Project build options. Si es así, cierra la actual y vuelve.



Selecciona la configuración \"default\" en la columna de la izquierda.
Asegúrate de que aparece seleccionado en el menú desplegable el compilador adecuado (\"Sega Genesis Compiler\" en el tutorial).
En la pestaña \"Make\" establece los siguientes valores:



Llegados a este punto es el momento de añadir archivos a nuestro proyecto recién creado.
En el caso de que queramos añadir un archivo ya creado sacaremos el menú contextual haciendo click con el botón derecho y seleccionando Add files....



O bien si lo que queremos es crear un nuevo archivo con código desde cero, haciendo click en el icono New File.



Hecho esto eligiremos la opción New empty file.
Cuando se nos pregunte, acto seguido, si deseamos vincularlo al proyecto responderemos que sí y le daremos un nombre (por ejemplo: main.c)
Para comprobar que todo se ha realizado correctamente escribiremos el siguiente código:

 
Código: [Seleccionar]
#include
 int main()
 {
       VDP_drawText(\\"Hello World!\\", 10, 13);
       return (0);
 }


Y compilaremos utilizando el atajo de teclado F9.

G - Archivo Binario de Salida (Rom)

El archivo binario resultado de la compilación debe de haberse creado dentro de una carpeta llamada out contenida en la carpeta que corresponda a nuestro proyecto. (En el presente ejemplo: prueba\\out\\rom.bin).
La abriremos y testearemos con cualquier emulador de megadrive o la pasaremos a un flashcart si queremos probarla en hardware real.

H - Enlaces Interesantes

Página oficial del kit de desarrollo para Megadrive SGDK:

http://code.google.com/p/sgdk/

SpritesMind Forum Foros dedicados a la programación de las consolas de SEGA:

http://gendev.spritesmind.net/forum/

Página web de Code::Blocks (apartado Downloads):

http://www.codeblocks.org/downloads/binaries

I - Deberes
Si, la idea es que tengáis que hacer deberes para afianzar conocimientos. En este tutorial con lograr configurar bien y lograr una rom ya es suficiente aunque no estaría de más que modificarais la instrucción VDP_drawText con otro texto y posición en pantalla trasteando sus parámetros. Tambien estaría bien que dierais un vistazo a los ejemplos que trae la libreria y los compilarais para ver que hacen o aprender/refrescar el lenguaje C ya que no es el objetivo de estos tutoriales ;)

TUTORIAL SGDK 02: CONTROLES

Bueno, pues ya estamos en el segundo tutorial, vamos con los controles.

Lo primero, como siempre, incluimos las librerías SGDK:

Código: [Seleccionar]
#include

A - Función MYJOYHANDLER()

En este ejemplo, aparte de  la función main vamos a tener otra, la MYJOYHANDLER().

Esta función se llamará en cada ocasión en que la videoconsola detecta la pulsación de un botón (para lo cual la pasaremos como parámetro más adelante a la JOY_setEventHandler()). Su cometido es interpretar los estados de entrada del pad y modificar estados o realizar acciones en nuestro juego, de momento nos conformaremos con dibujar distintas cadenas de texto en pantalla para durante la ejecución conocer los estados de los botones y direcciones del pad ;)

Código: [Seleccionar]
void myJoyHandler( u16 joy, u16 changed, u16 state)
{
    //Si el botón pulsado corresponde al pad en el puerto 1
if (joy == JOY_1)
{
   //La sintaxis del código para comprobar el estado
   //del botón será la que sigue variando el valor
   //con el que se compararán sendos atributos:
   //state y change, state correspondiéndose con la
   //pulsación del botón y change con la liberación
   //del mismo
if (state & BUTTON_START) //Si se pulsa START
{
   //Que específicamente elijamos estas determinadas
   //coordenadas (x=5, y=13) se debe a que en la
   //función main vamos a mostrar el rótulo
   //correspondiente al estado del botón START en
   //dichas coordenadas. Lo que lo que estaremos
   //haciendo será sobreescribir el rótulo cada vez
   //que se pulse o suelte el botón, ya sea mostrando
   //\\"START button 1\\" o \\"START button 0\\" según el caso
VDP_drawText(\\"START button 1\\", 5, 13);
//El área para disponer texto en pantalla en
//megadrive se corresponde exactamente con el área
//de tiles visibles en pantalla (no hay razón para
            //no considerar cara caracter un tile, que es lo
            //que también son). Por lo tanto, en el modo PAL
            //\\"normal\\" en que disponemos de una resolución de
            //320x224 tendremos un área de 40x28 caracteres;
            //La primera fila y la primera columna toma valor
            //cero, por lo que para comenzar un texto en la
            //esquina superior izquierda lo haríamos tal que
            //así: VDP_drawText(\\"texto\\", 0, 0);
}
else if (changed & BUTTON_START) //Si se suelta
{
VDP_drawText(\\"START button 0\\", 5, 13);
}

if (state & BUTTON_A) //Si se pulsa A
{
VDP_drawText(\\"A button 1\\", 5, 14);
}
else if (changed & BUTTON_A) //Si se suelta A
{
VDP_drawText(\\"A button 0\\", 5, 14);
}

if (state & BUTTON_B)
{
VDP_drawText(\\"B button 1\\", 5, 15);
}
else if (changed & BUTTON_B)
{
VDP_drawText(\\"B button 0\\", 5, 15);
}

if (state & BUTTON_C)
{
VDP_drawText(\\"C button 1\\", 5, 16);
}
else if (changed & BUTTON_C)
{
VDP_drawText(\\"C button 0\\", 5, 16);
}

if (state & BUTTON_X)
{
VDP_drawText(\\"X button 1\\", 17, 14);
}
else if (changed & BUTTON_X)
{
VDP_drawText(\\"X button 0\\", 17, 14);
}

if (state & BUTTON_Y)
{
VDP_drawText(\\"Y button 1\\", 17, 15);
}
else if (changed & BUTTON_Y)
{
VDP_drawText(\\"Y button 0\\", 17, 15);
}

if (state & BUTTON_Z)
{
VDP_drawText(\\"Z button 1\\", 17, 16);
}
else if (changed & BUTTON_Z)
{
VDP_drawText(\\"Z button 0\\", 17, 16);
}


if (state & BUTTON_UP)
{
VDP_drawText(\\"UP button 1\\", 5, 17);
}
else if (changed & BUTTON_UP)
{
VDP_drawText(\\"UP button 0\\", 5, 17);
}

if (state & BUTTON_DOWN)
{
VDP_drawText(\\"DOWN button 1\\", 5, 18);
}
else if (changed & BUTTON_DOWN)
{
VDP_drawText(\\"DOWN button 0\\", 5, 18);
}

if (state & BUTTON_LEFT)
{
VDP_drawText(\\"LEFT button 1\\", 5, 19);
}
else if (changed & BUTTON_LEFT)
{
VDP_drawText(\\"LEFT button 0\\", 5, 19);
}

if (state & BUTTON_RIGHT)
{
VDP_drawText(\\"RIGHT button 1\\", 5, 20);
}
else if (changed & BUTTON_RIGHT)
{
VDP_drawText(\\"RIGHT button 0\\", 5, 20);
}
}


// otras funciones interesante
// JOY_update() refresca el estado del pad, se llama en cada refresco de la pantalla
    // JOY_readJoypad( joy ) ¨devuelve el estado del pad1
    // JOY_waitPressBtn() espera a que se pulse un boton (no direcciones)
    // JOY_waitPress(joy, BUTTON_A | BUTTON_UP) espera a pulsar un boton indicado en un pad especifico

}//end myJoyHandler()


B - Función MAIN()

Ahora volvemos a la función main(), donde iniciamos el manejador de los controles, establecemos la función de control myJoyHandler() anterior que se ejecutará automáticamente y entramos en un bucle infinito.

Código: [Seleccionar]
////////////////////////////////////////////////////////////////////
// MAIN
////////////////////////////////////////////////////////////////////
int main( )
{
// resetea el manejador y lee la informacion de los mandos conectados
JOY_init();

    //Establecemos que el programa dará soporte al pad de 6 botones
    //conectado en el puerto 1
JOY_setSupport(PORT_1, JOY_SUPPORT_6BTN);
    //Los otros valores correspondientes al resto de controles soportados
    //en las librerías SGDK son JOY_SUPPORT_OFF, JOY_SUPPORT_3BTN,
    //JOY_SUPPORT_MOUSE, JOY_SUPPORT_TRACKBALL, JOY_SUPPORT_TEAMPLAYER,
    //JOY_SUPPORT_EA4WAYPLAY, JOY_SUPPORT_MENACER, JOY_SUPPORT_JUSTIFIER_BLUE,
    //JOY_SUPPORT_JUSTIFIER_BOTH, JOY_SUPPORT_ANALOGJOY, JOY_SUPPORT_KEYBOARD

//A través de la función siguiente conseguimos que nuestra funcion
//creada anteriormente sea llamada cada vez que se cambia el estado de un boton
JOY_setEventHandler( &myJoyHandler );

    //Dibujamos con dos líneas de texto una cabecera austera
VDP_drawText(\\"Ejemplo de control del PAD\\", 5, 10);
VDP_drawText(\\"--------------------------\\", 5, 11);

    //Y rellenamos el resto del espacio escribiendo cadenas que indiquen
    //que inicialmente todos los estados están a cero (nada pulsado)
VDP_drawText(\\"START button 0\\", 5, 13);
VDP_drawText(\\"A button 0\\", 5, 14);
VDP_drawText(\\"B button 0\\", 5, 15);
VDP_drawText(\\"C button 0\\", 5, 16);
VDP_drawText(\\"X button 0\\", 17, 14);
VDP_drawText(\\"Y button 0\\", 17, 15);
VDP_drawText(\\"Z button 0\\", 17, 16);

VDP_drawText(\\"UP button 0\\", 5, 17);
VDP_drawText(\\"DOWN button 0\\", 5, 18);
VDP_drawText(\\"LEFT button 0\\", 5, 19);
VDP_drawText(\\"RIGHT button 0\\", 5, 20);

    //BUCLE PRINCIPAL
    //En este caso sólo tendrá en cuenta el manejador del input que
    //hemos creado y establecido con JOY_setEventHandler anteriormente
    while(1){

        // Sincroniza la pantalla de modo que el barrido vertical no
        // interfiera con el dibujo de tiles, sprites o nuestro texto,
        // evitándonos algunos efectos indeseados de flickering
        // principalmente
        VDP_waitVSync();
    }

    return (0); // Sin efecto funcional en nuestro juego o programa,
                // pero de obligada aparición al final de una función
                // main que no se ha declarado como \\"void main()\\"

}//end main()


Y ya está, ya tenemos control de todos los botones del PAD1, aquí os dejo un fichero con el código por si no teneis ganas de teclear  [+risas]

https://dl.dropbox.com/u/33369593/main.c

C - Deberes

Como deberes, añadir soporte para el mando del segundo jugador. En el próximo tutorial, empezamos con los gráficos, dibujaremos tiles en pantalla!

TUTORIAL SGDK 03: TILES

Para aquel que no esté familiarizado con los \"tiles\", vamos a partir entonces de su traducción al castellano: \"azulejos\", \"losetas\" o \"baldosas\", pero nos quedaremos con el término \"tiles\" para no liarnos.

Todo lo que vemos plasmado en pantalla cuando encendemos una megadrive está generado en base a tiles (la excepción que usa la \"magia\" de una técnica llamada framebuffer quedará para otro día). Esto es, que todos los fondos que vemos están formados por trocitos de ocho por ocho píxels que en su conjunto forman el escenario y los sprites.

En modo PAL \"estándar\" el área visible se rellena con 40x28 tiles (horizontal x vertical). En la VRAM, la memoria de vídeo de la consola, habrán de almacenarse todos los patrones distintos de tiles que vayamos a usar. Por lo tanto trabajar con tiles no es más que componer escenarios (en el presente caso, otro dia sprites) usando repeticiones bien pensadas de pedacitos gráficos de ocho por ocho píxels.

Con eso tenemos para empezar sin perdernos demasiado, así que manos a la obra...

Incluimos las librerías SGDK como en otros tutos:
Código: [Seleccionar]
#include

Incluimos los tiles generados con SGDK, colocamos la imagen que queremos convertir en tiles en la carpeta \"res\" de nuestro proyecto. Sus dimensiones han de ser ambas múltiplos de 8 y debe ser un bitmap de 16 colores (4 bits/pixel). Momento para echar un vistazo a la carpeta del proyecto, abrir \"res\" y comprobar lo comentado.

Código: [Seleccionar]
#include \\"moon.h\\"

Creamos \"a pelo\" un tile (ahora lo explicamos). Con el permiso de los que ya se manejan con C diremos que el trocito de código que sigue es para crear un dato de tipo array (AKA \"vector\", AKA \"tabla\" ...), que no es otra cosa que un dato que contiene a su vez una ristra de datos. Nuestro array de nombre \"tile\" contendrá ocho elementos, ocho datos de 32 bits, y cada uno de ellos se corresponderá con cada una de las líneas horizontales que forman el tile (1 tile = 8x8 píxels).

Los datos están representados en base hexadecimal (se preceden con 0x para indicarlo) y son separados por comas.

Llegó el momento de revelar una de las características que más quebraderos de cabeza darán al grafista de turno, los tiles en megadrive están limitados a 16 colores (siendo el cero transparente, así que ... 15). Lo que va a ser un poco jodienda cuando nos metamos en materia artística, por lo menos para representar los tiles en formato de array viene a ser una ventaja pues se puede ver con relativa claridad el contenido gráfico del tile mirando su código (\"relativa\")

Si nos acordamos de que 0x es sólo un prefijo para indicar valor hexadecimal (base numérica en la que al llegar a 9 seguimos A, B, C, D, E, F y luego ya 10 -que equivaldría al 16 decimal-) cada valor representa en orden de izquierda a derecha y de arriba a abajo los colores de los píxels que conforman nuestro tile (color cero, color cero, color uno, color uno ...) ¿Y qué colores son esos? ¡Ah, amigo! eso depende de la paleta de color, anticiparemos que el color cero es el \"color transparente\", el que no se pinta, vamos; el resto en nada lo vemos.

Código: [Seleccionar]
const u32 tile[8]=
{
                0x00111100, // Línea 1: pixels 1 a 8
                0x01144110, // Línea 2
                0x11244211, // Línea 3
                0x11244211, // Línea 4
                0x11222211, // Línea 5
                0x11222211, // Línea 6
                0x01122110, // Línea 7
                0x00111100  // Línea 8: píxels 57 a 64
};


SGDK ofrece dos métodos de convertir imágenes a tiles sin tener que andarse con el trabajo de chinos que supone crear los tiles a pelo como hemos hecho anteriormente. Para ello o bien colocamos el bitmap que queramos \"tilear\" en la carpeta \"res\" de nuestro proyecto. O bien, haciendo uso de la herramienta \"genres\" también incluída que nos requiere editar el archivo \"resource.rc\" para indicar qué imágenes queremos en este caso convertir.

Si vamos a usar el método de \"genres\" precisamos contar con el siguiente struct para manejar los tiles. Para no alargarnos, recomiendo hacer una pausa aquí para echar una ojeada al archivo \"resource.rc\" de este ejemplo:

Código: [Seleccionar]
struct genresTiles
{
u16 *pal; // Puntero a los datos de la paleta
u32 *tiles; // Puntero a los datos de los tiles
u16 width; // Ancho en tiles
u16 height; // Alto en tiles
u16 compressedSize; // 0 en esta demo, mas proximamente
};


Como con los tiles generados con SGDK:
 - Las dimensiones de los bitmap han de ser múltiplos de 8
 - Deben ser un bitmaps de 16 colores (4 bits/pixel)

Y con esta línea \"cargamos\" en nuestro código los tiles que \"genres\" nos haya generado. (Los cargamos en una estructura de tipo \"genresTiles\" que para algo la creamos justo antes ;)

Código: [Seleccionar]
extern struct genresTiles luna;

Tambien existe la herramienta imagenesis que conoceréis si mirasteis el tutorial de basic de theelf, la pega es que el formato que exporta no nos sirve pero vamos a ver como solucionarlo. Abrimos la imagen (file, open image), elegimos el tipo de conversión (si queremos optimizar los tiles -> mode, 15 color 4bpp 1 plane 8x8 tiles optimized, si es un sprite tendremos que cambiar el orden x-y... lo veremos en el tutorial siguiente sobre estos últimos), el color transparente (enable transparency, select transparent color). Pulsamos Actions -> Quantize Now y tendremos la imagen convertida a la paleta de megadrive y lista para exportar. La paltea si la podemos exportar sin modificarla con Export Palette Data y eligiendo como salida C, el mapa de tiles lo mismo, ya veremos como usarlo en posteriores tutoriales. El problema son los tiles, para usaremos el pequeño conversor que he escrito, el megagfx. Exportamos los tiles como basic y nos fijamos en el número de tiles que se han generado, lo pegamos en el fichero output.txt que viene en el rar que adjunto, pulsamos run.bat, elegimos un nombre para el vector de salida y por último la cantidad de tiles a covertir que vimos al convertirla en imagenesis. Se generará un fichero llamado tiles.txt, cuyo contenido podréis copiar sin problema en vuestro código y que será completamente compatible con las funciones de manejo de tiles de las SGDK.

Seguimos, como en el tutorial anterior, cabecera austera con VDP_drawText

Código: [Seleccionar]
////////////////////////////////////////////////////////////////////
// MAIN
////////////////////////////////////////////////////////////////////
int main( ){

    VDP_drawText(\\"Ejemplo de dibujo de TILES\\", 5, 1);
VDP_drawText(\\"--------------------------\\", 5, 2);


Cargamos nuestro tile \"creado a pelo\" en la posicion 1 de la VRAM:

Código: [Seleccionar]
// VDP_loadTileData(const u32 *data, u16 index, u16 num, u8 use_dma):
//  - data:    puntero a los datos de nuestro tile
//   (o al primero de nuestra ristra tiles si es el caso)
//  - index:   en qué posición de la memoria de vídeo (VRAM) vamos a
//   almacernarlo (o a partir de qué dirección si son varios)
//  - num:     cuántos tiles vamos a almacenar a partir de esa posición
//  - use_dma: ¿usaremo o no \\"Acceso Directo a Memoria\\"?
    VDP_loadTileData( (const u32 *)tile, 1, 1, 0);


En cuanto a la paleta de color, usaremos una de las paletas por defecto. Disponemos en megadrive de 4 paletas simultáneas de 15 colores cada una (+ transparente), por defecto la primera paleta (PAL0) contiene una escala de grises, PAL1: escala de rojos, PAL2: verdes, PAL3: azules.

Buen momento éste para regresar a donde creamos nuestro tile \"a pelo\", de \"0 a F\" van 16 valores y nuestras paletas son de quince colores más el \"color transparente\" (que se corresponde con el valor cero). Cada valor corresponde con el color que ha de tomar un píxel de acuerdo a los que estén disponibles en la paleta que corresponda a este tile, luego ya, si eso, puedes experimentar cambiando valores.

Si antes habíamos almacenado nuestro tile en la memoria de vídeo, ahora es el momento en el que lo sacamos de allí para ponerlo en pantalla.

Dibujamos...
    ... el tile que se encuentre en la POSICIÓN 1 de la VRAM
    ... sobre el \"PLANO A\"
    ... en las COORDENADAS (tile 5 horizontal,tile 5 vertical) (origen: 0,0)
    ... (con la paleta 0)
   
Código: [Seleccionar]
VDP_setTileMap(APLAN, 1, 5, 5);

\"Plano A\": la megadrive por hardware cuenta con dos planos de profundidad sobre los que, como norma general, dibujaremos los fondos rellenándolos de lustrosos tiles. El \"plano A\" (referenciado en SGDK con el nombre APLAN) es el más próximo, siendo BPLAN al que se le superpone éste (por norma general). En el futuro podremos modificar las prioridades de los sprites respecto a los planos o incluso usar el plano WINDOW, que no es más que un área del plano A que será fijo y no se moverá con el scroll, se suele utilizar para marcadores y cosas por el estilo.

Hacemos lo mismo pero asignándole parametros al tile ayudándonos de la macro TILE_ATTR_FULL() que insertaremos como segundo parámetro en la llamada a VDP_setTileMap().

Los valores de los atributos de la macro TILE_ATTR_FULL() son:
     PAL2 = paleta predeterminada verde
     0 = baja prioridad (se dibujará bajo tilemaps de alta prioridad)
     1 = vflip (reflejo vertical)
     0 = no hflip (no se usa reflejo horizontal)
     1 = tile 1 de la VRAM

Código: [Seleccionar]
VDP_setTileMap(BPLAN, TILE_ATTR_FULL(PAL2, 0, 1, 0, 1), 6, 5);

Vamos a experimentar con las prioridades, un mismo tile puede tener distintas propiedades independientemente del plano en el que esté si tienen la misma prioridad, A prevalece sobre B:

Código: [Seleccionar]
   VDP_setTileMap(APLAN, TILE_ATTR_FULL(PAL1, 1, 0, 0, 1), 7, 7);
    VDP_setTileMap(BPLAN, TILE_ATTR_FULL(PAL2, 0, 0, 0, 1), 7, 7);
    VDP_setTileMap(APLAN, TILE_ATTR_FULL(PAL1, 0, 0, 0, 1), 8, 7);
    VDP_setTileMap(BPLAN, TILE_ATTR_FULL(PAL2, 1, 0, 0, 1), 8, 7);



Y si hasta ahora VDP_setTileMap() nos ha servido para dibujar en pantalla nuestro tile de forma independiente en las coordenadas que indicásemos... \"VDP_fillTileMapRect()\" va a hacer lo propio rellenando un determinado área mediante repeticiones de ese tile.

Código: [Seleccionar]
   // Rellena un cuadrado de 8x4 del tile 1 con paleta azul en (12,12)
    VDP_fillTileMapRect(BPLAN, TILE_ATTR_FULL(PAL3, 0, 0, 0, 1), 12, 12, 8, 4);


Como veis, los parámetros son los mismos que en la función anterior, con el añadido de cuántos tiles en horizontal y en vertical se dispondrán en las coordenadas dadas

 A lo mejor llegado a este punto necesitas una cerveza para seguir en predisposición de seguir adquiriendo conocimiento megadrivero, sin problema, busca en el frigo y ahora seguimos con lo propio para los tiles generados por SGDK, queda poco en todo caso. Lo más normal si hemos dejado una imagen para que nos la convierta SGDK en tiles utilizables en nuestro código, es que esa conversión nos ofrezca \"varios tiles\", por lo que los anteriores ejemplos usando uno solo van a variar un poquito.

 Casi al principio de nuestro código escribíamos la siguiente línea: #include \"moon.h\"

Con ello disponemos de un array \"moon[]\" el cual contiene todos los datos necesarios para que hagamos uso de la imagen original ya oportunamente \"tiletizada\"

El primer elemento del array contiene el ancho en píxels de la imagen,  lo guardamos en la variable \"w\" (width) pasado a tiles (dividiendo entre ocho):

   
Código: [Seleccionar]
u16 w = moon[0] / 8;
   
 El segundo elemento del array contiene el alto en píxels de la imagen, lo guardamos en la variable \"h\" (height) pasado a tiles (dividiendo entre ocho) :

   
Código: [Seleccionar]
u16 h = moon[1] / 8;

Cargamos sobre PAL1 la paleta de la imagen que está almacenada en moon[2 a 17]

 
Código: [Seleccionar]
  VDP_setPalette(PAL1, &moon[2]);

 Cargamos los datos del bitmap en la VRAM usando para ello la función VDP_loadBMPTileData(const u32 *data, u16 index, u16 w, u16 h, u16 bmp_w)
   
        Argumentos
    - data:  dirección de memoria a partir de la cual se encuentra la información gráfica
    - index: posición en VRAM a partir de la cual se almacenarán los tiles
    - w:     ancho (en tiles) de la región del bitmap que mandaremos a VRAM
         - h:     alto (en tiles) de la región del bitmap que mandaremos a VRAM
    - bmp_w: ancho (en tiles) de la imagen original; éste último argumento es necesario porque podríamos cargar solo un a parte del bitmap pero SGDK necesita el ancho como referencia.

 
Código: [Seleccionar]
  VDP_loadBMPTileData((u32*) &moon[18], 2, w, h, w);

Y finalmente dibujamos el bitmap en pantalla. De nuevo hacemos uso de TILE_ATTR_FULL() para indicar los atributos extras que queremos asignar a la  imagen, principalmente nos interesa que haga uso de PAL1 por ser la paleta donde hemos cargado los colores de la imagen original.

(23, 6) son las coordenadas en pantalla (en tiles; \"1 tile = 8 x 8 píxels\")
(w, h) son el ancho y el alto (en tiles) respectivamente

Código: [Seleccionar]
VDP_fillTileMapRectInc(BPLAN, TILE_ATTR_FULL(PAL1, 0, 0, 0, 2), 23, 6, w, h);

 Y ya sí, por fin. Lo propio para tiles generados por genres que habían sido incluidos en nuestro código mediante la siguiente línea \"extern struct genresTiles luna;\"

Guardamos la paleta de la imagen sobre PAL2, tomándola del atributo \"pal\"
 
 
Código: [Seleccionar]
VDP_setPalette(PAL2, luna.pal);

 Cargamos en VRAM todos los tiles que han resultado de la conversión de la imagen:

         Argumentos de VDP_loadTileData()
     arg0 = dirección donde se encuentran los tiles a cargar
     arg1 = indice (posición en VRAM) del primero de los tiles que conforman la imagen
     arg2 = núero de tiles a enviar a la VRAM
     arg3 = usar DMA (1) o no (0)

   
Código: [Seleccionar]
VDP_loadTileData(luna.tiles, 100, luna.width*luna.height, 0);
   
Y ya sólo nos queda dibujar la imagen en pantalla usando la misma función que en el ejemplo de tiles generados por el SGDK, VDP_fillTileMapRectInc().  En este caso usando PAL2, indicando que tomaremos tiles que se encuentran a partir de la posición 100 de la VRAM, en las coordenadas (23,12) de pantalla y con un ancho y un alto (en tiles) indicados por los atributos \"width\" y \"heigh\" del struct luna:

   
Código: [Seleccionar]
VDP_fillTileMapRectInc(BPLAN, TILE_ATTR_FULL(PAL2, 0, 0, 0, 100), 23, 12, luna.width, luna.height);

   Bucle infinito
   
Código: [Seleccionar]
while(1){

        // sincroniza la pantalla
        VDP_waitVSync();
    }


 Y ¡chin pon!
   

Aquí os dejo el fichero con el código y los gráficos utilizados, además otro con el comentado megagfx.
https://dl.dropbox.com/u/33369593/tuto_megadrive/03_tiles.rar
https://dl.dropbox.com/u/33369593/tuto_megadrive/megagfx.rar



Como deberes... entender el tutorial, que no es poco, trastear con él y cambiar los gráficos por unos vuestros. Lo próximo... sprites!


TUTORIAL DE SGDK 04: SPRITES

Pues aquí andamos de nuevo con un nuevo tuto que en conjunción con el anterior, en el que tratamos los tiles, va a empezar a darle vidilla a las primeras demos que os animéis a hacer.

Eso sí, si aún no te ha quedado del todo claro el funcionamiento y uso de tiles en megadrive es MUY recomendable volver al tuto anterior y dedicarle el tiempo necesario. Tampoco hay por qué agobiarse, no tiene ma? historia que:

 1.Crear los tiles con cualquier método disponible
 2.Mandarlos a VRAM en una determinada posición
 3.Dibujarlos en pantalla usando la función pertinente


 Pero claro, hay que conocer las funciones necesarias y conviene tener claro también como funciona en líneas generales la memoria de vídeo. Se reitera, no tiene gran misterio, sólo requiere un poquito de paciencia y tal vez una pizca de concentración para pillar la idea de primeras.

 Y tras habernos ido por las ramas para no pasar por alto que este tuto parte de la base de haber asimilado debidamente el anterior, damas y caballeros, con todos
 ustedes \"Los Sprites\".

 (Insertar FX \"Ovación Multitud\"  ZzzZZ )

 \"Esto ... ¿y si yo no sé que es un sprite?\"
 Si ese es el caso, vamos con unos ejemplos visuales la mar de efectivos y así nos ahorramos la definición técnica que para eso se tira de wikipedia y listo.

 Primero recordaremos que megadrive dispone de dos planos de fondo por hardware: plano A, plano B. Para por ejemplo dibujar en el B un mapa de tiles que contengan las montañas en la lejanía y en el A usamos tiles para dibujar una cordillera cercana. Llegado el momento haremos que el plano B se desplace más despacio que el A, horizontalmente los dos (tómate tus segundos para visualizar la idea, no hay prisa).

Y entonces a lo mejor, luego de visualizar mentalmente el ejemplo, piensas: \"Oye, ¿se supone que ibas a hablar sobre sprites?\". Sí, pero me viene al pelo el ejemplo de los mapas de tiles. Hagámonos a la idea de que los \"mapas de tiles\" dibujados en los planos A y B nos servirán para dibujar fondos y escenarios de nuestros juegos. Los \"sprites\" suelen ser los objetos, personajes o elementos que aunque lleguen a interactuar con el escenario no forman parte de éste.

 Ejemplo: Sonic, Green Hill Zone
  - \"Mapa de tiles\" en el plano A: el escenario
      plataformero en primer plano

  - \"Mapa de tiles\" en el plano B: el mar, bosquecillos
      cascadas, montañas, nubes y cielo en segundo plano
      (que se muevan independientemente es algo de lo
       tal vez hablaremos en otro tuto)

  - SPRITES: Sonic, los monitores, enemigos, anillos ...

 En un juego de naves los sprites serán las propias naves, los bichejos enemigos, las explosiones, disparos y demás elementos que sean independientes del escenario.
 Los sprites se dibujan sobre el \"plano de sprites\". Con lo cual ya tenemos que la megadrive dispone de:

 - 2 planos para fondo (A y B)
 - 1 plano para sprites
 - 1 ?? (trollface), \"plano Window\", que en realidad será una parte inmóvil del plano A que sirve normalmente para meter los marcadores, etc.

 Y ya por último antes de meternos en faena, enumeraremos algunas características (o puñeterías) de los sprites en megadrive.

 - El plano destinado a los sprites tiene un tamaño fijo y no permite scroll. Normalmente 512x512 píxels, correspondiendo la coordenada 128,128 al primer píxel visible en la esquina superior izquierda de la pantalla. Con SGDK usaremos 0,0 como coordenada que indique el píxel de la esquina superior izquierda del plano de sprites, pero la consola en realidad no los maneja así internamente.

 - Cualquier sprite fuera del área visible de pantalla no será visible. Puesto de otro modo, si sitúas un sprite de 16 píxels de ancho en la coordenada (-20, 128) dicho sprite no aparecerá en pantalla pero estar.. está.

 - Si el valor dado a la coordenada X o Y del sprite excede el espacio del plano \"vuelve\" sobre el origen. Ejemplo: (300, 812) es lo mismo que (300, 300)

 - Todos los sprites están identificados con un índice numérico y cuentan con un atributo que indica cuál es el sprite que le sigue a la hora de ser dibujado (\"link\"). El atributo link de nuestro último sprite ha de tomar el valor del índice que tenga el primer sprite que vayamos a dibujar. Si no lo hacemos así obtendremos el bonito número de cero sprites en pantalla. Podemos modificarlos más tarde para indicar prioridades entre los sprites... cual se dibuja encima de otros, imaginad los dragones del Space Harrier, cuyas partes cambian de prioridad según se acercan o alejan de la pantalla, para eso sirve.

 - Máximo 80 sprites definidos simultáneamente.
 - Máximo 20 sprites sobre una misma scanline (\"en línea\"), si te pasas... parpadean.

 No es necesario aprendérselas de memoria, pero si tu demo o tu juego hace algo raro y no sabes por qué, dales un repaso a ver si algo de eso va a tener la culpa.

 Y la idea básica y más importante:

 - Los sprites están compuestos por tiles, podemos definir sprites desde 1x1 tile (8x8 píxels) hasta 4x4 tiles (32x32 píxels) usando cualquier valor intermedio, cosa que la todopoderosa snes no puede  [poraki]

   Por ejemplo (3x2 tiles = 24x16 píxels).
   
En el momento en que quieras usar sprites más grandes, ten en cuenta que megadrive cuenta con esos límites, por lo que tendrás que:

    . definir varios sprites hasta completar el tamaño que necesites
    . y tratar las coordenadas de esos sprites de manera adecuada para que en su conjunto formen la imagen que hayan de representar.

 Vamos al lío ...

Código: [Seleccionar]


****************************************************/

// LIBRERIAS ////////////////////////////////////////
#include

// DATOS GLOBALES ///////////////////////////////////


Cuando programando con SGDK definamos algún dato como const (\"constante\", el valor con que se inicialice ya no puede variar en todo el programa) dicho dato se amacenará en ROM en lugar de en la memoria RAM de 64Kb, así que los gráficos por ejemplo DEBEN declararse así.

Vamos a crear un array de cuatro tiles (2x2) que representarán la imagen de una bola. Éste será el gráfico de nuestro primer sprite. Pero aún queda mandanga después de definir el array. Paciencia que con esto simplemente tenemos la información gráfica, que ya es algo por otro lado.

Código: [Seleccionar]
const u32 spriteTiles[4*8]=
{
0x00001111, // Tile izquierda arriba
0x00001111,
0x00111144,
0x00111144,
0x11112244,
0x11112244,
0x11112244,
0x11112244,

0x11112222, // Tile izquierda abajo
0x11112222,
0x11112222,
0x11112222,
0x00111122,
0x00111122,
0x00001111,
0x00001111,

0x11110000, // Tile derecha arriba
0x11110000,
0x44111100,
0x44111100,
0x44221111,
0x44221111,
0x44221111,
0x44221111,

0x22221111, // Tile derecha abajo
0x22221111,
0x22221111,
0x22221111,
0x22111100,
0x22111100,
0x11110000,
0x11110000
};


Del mismo modo que en el tutorial anterior tuvimos que crear un struct \"genresTiles\" (sí, lo hicimos, si no te acuerdas, anda, búscalo y de le das el repasito) para manejar los tiles que nos creaba la herramienta \"genres\" provista por SGDK, en este caso haremos lo propio creando un struct que nos permitirá hacer buen uso de los sprites. Guarda gran similitud con \"genresTiles\" pero una importante diferencia a tener en cuenta es que los valores que usemos vendrán dados en píxels.

 Le echamos un ojo:

Código: [Seleccionar]
struct genresSprites
{
u16 *pal; // Puntero a los datos de la paleta
u32 **sprites; // Puntero a los datos de los sprites
u16 count; // Numero de sprites
u16 width; // Ancho de cada sprite en pixels
u16 height; // Ancho de cada sprite en pixels
u16 size; // como usamos el ancho/alto en pixels, informacion importante en el tamaño del sprite

};


Almacenamos los datos del exprite que nos haya generado \"genres\" sobre los atributos de un struct \"genresSprites\".

Código: [Seleccionar]
extern struct genresSprites sonic;

En el archivo \"resource.rc\" se habrá indicado el nombre de la variable de salida (sonic), la ruta del bitmap que contiene los frames de nuestro sprite y el tamaño en píxels en el que se han de dividir los distintos frames.  En nuestro caso, de la siguiente manera:

 
Código: [Seleccionar]
SPRITE sonic \\"data/sonic.bmp\\" 24 32 0 7

Empecemos con el bucle principal, como en las otras ocasiones:

Código: [Seleccionar]
/////////////////////////////////////////////////////////////
// MAIN
/////////////////////////////////////////////////////////////
int main( ){

// Dibujamos la cabecera austera de turno con VDP_drawText()
    VDP_drawText(\\"Ejemplo de dibujo de SPRITES\\", 5, 1);
VDP_drawText(\\"----------------------------\\", 5, 2);


Igual que hicimos en el tuto anterior, hay que empezar por escribir los tiles que vayamos a requerir en la VRAM de la consola usando VDP_loadTileData(). La recordamos:
   
   
Código: [Seleccionar]
// VDP_loadTileData(const u32 *data, u16 index, u16 num, u8 use_dma):
//  - data:    puntero a los datos de nuestro tile
//   (o al primero de nuestra ristra tiles si es el caso)
//  - index:   en qué posición de la memoria de vídeo (VRAM) vamos a
//   almacernarlo (o a partir de qué dirección si son varios)
//  - num:     cuántos tiles vamos a almacenar a partir de esa posición
//  - use_dma: ¿usaremos o no \\"Acceso Directo a Memoria\\"?

        // \\"spriteTiles\\" es el nombre que le dimos al array con la información
// gráfica de nuestro sprite-bola de 2x2 tiles.
        VDP_loadTileData( (const u32 *)spriteTiles, 1, 4, 1);


La llamada a esta función es opcional, pero es recomendable usarla para asegurarnos de que ningún sprite arranque con algún valor que no le hayamos dado nosotros y el consecuente desespero

   
Código: [Seleccionar]
VDP_resetSprites();

Digamos que por sistema, tras mandar a VRAM los tiles de un sprite, lo siguiente por hacer es cargar su paleta. Nuestro sprite-bola usará una de las predefinidas sin embargo.

Y ahora... Y AHORA ... Y AHOOOORA... lo definimos :)

Lo que viene a decir que le indicaremos a la megadrive que queremos crear un flamante sprite con las características que le indiquemos y ya que ella se encargue de rellanar los registros en una tabla contenida en la VRAM con los valores pertinentes.

Cada sprite que pensemos usar hemos de definirlo antes de poder hacer uso de él en el programa. Vamos a ver dos métodos de hacerlo y luego ya decides tú cuál te parece más conveniente (*cof*, el segundo, *cof*)
   
 Primero veamos cómo definir un sprite usando la función básica que SGDK dispone para ello de modo, VDP_setSprite() :
   
   
Código: [Seleccionar]
// void VDP_setSprite(u16 index, u16 x, u16 y, u8 size, u16 tile_attr, u8 link)
// - index:     índice numérico que identifica al sprite (desde 0 a 79)
    // - x:         coordenada x en pantalla en la que se situará
    // - y:         coordenada y en pantalla en la que se situará
    // - size:      tamaño en tiles (desde 1x1 a 4x4 tiles). Nos valdremos de la
//              macro \\"SPRITE_SIZE()\\" para indicarlo
    // - tile_attr: atributos de los tile(s), (paleta, prioridad, vflip, hflip, pos_tile_VRAM)
    // - link:      índice del sprite que sucede al actual, cada sprite debe enlazar
//              al siguiente, teniendo el último que enlazar con el primero
    VDP_setSprite(0, 40, 40, SPRITE_SIZE(2,2), TILE_ATTR_FULL(PAL0,1,0,0,1), 1);


Atendiendo a los valores que hemos pasado como parámetros podemos ver que nuestro primer sprite (sprite 0), tiene un tamaño de 2x2 tiles los cuales se tomarán a partir de la posición 1 de la VRAM. Ergo, nuestro primer sprite va a mostrar el gráfico de la bola que creamos al empezar el código. Una cosa a tener en cuenta, es que mientras que para dibujar tiles en uno de las capas A y B, la consola dibuja los tiles secuencialmente de izquierda a derecha y de arriba a abajo, para los sprites lo hace de arriba a abajo y de izquierda a derecha, hay que tenerlo en cuenta a la hora de meterlo en la vram, pero vamos, que de eso se encargará el conversor gráfico que usemos al especificar que es un sprite, ya sea genres, imagenesis, etc.

Veamos ahora otra manera distinta (¿más elegante?) para definir un sprite

 
Código: [Seleccionar]
  SpriteDef spriteBola2;

\"SpriteDef\" es un struct disponible en SGDK destinado a acoger los distintos datos que precisa un sprite. El primer paso de este método es proceder como en la línea de código anterior: simplemente creando un dato de tipo SpriteDef que nombraremos según nos plazca. La definición de SpriteDef en SGDK es la siguiente:
   
     
Código: [Seleccionar]
 // typedef struct
// {
//   s16 posx, posy;
//   u16 tile_attr;
//   u8 size, link;
// } SpriteDef;
// Los atributos se corresponden con cada uno de los parámetros requeridos por
// VDP_setSprite() salvando la ausencia de uno para el índice del sprite


Creado el dato, inicializamos sus atributos.

 
Código: [Seleccionar]
  spriteBola2.posx = 0;
    spriteBola2.posy = 0;
    spriteBola2.size = SPRITE_SIZE(2,2);
    spriteBola2.tile_attr = TILE_ATTR_FULL(PAL0,1,0,0,1);
    spriteBola2.link  = 2;


Y llegado este momento usamos la función VDP_setSpriteP() para definir nuestro segundo sprite:
   
   
Código: [Seleccionar]
// void VDP_setSpriteP(u16 index, const SpriteDef *sprite)
// - index:  índice numérico que identifica al sprite (desde 0 a 79)
// - sprite: dirección de memoria donde se encuentra el struct
//           con la información de nuestro sprite
//           (nos valemos del operador & para ello)
    VDP_setSpriteP(1, &spriteBola2);


Está siendo un poco tocho pero es por pretender dejar clara la forma de proceder para crear sprites. Ahora que tenemos ya definidos dos sprites, un par de bolas que se desplazarán (o no) por pantalla, vamos a recapitular a modo de resumen los pasos para crear y mostrar sprites en pantalla:

    1. Cargar los tiles necesarios en VRAM
    2. Cargar la paleta (si no ha sido cargada previamente)
    3. Definir el sprite
    4. Definir más sprites
    5. Solicitarle a la megadrive que nos los dibuje


Vamos a ver ya por último como definir us sprite creado desde genres y en unos minutos nos metemos POR FIN en cómo animarlos por pantalla, tanto en cuanto a desplazarlos por la pantalla como crear el efecto de animación intercambiando frames. Pero antes, lo dicho: definir sprites creados con genres.

La puñeta siguiente es porque nos va a interesar conocer el número de tiles de los sprites en el \"spritesheet\" (el bitmap con los frames de nuestro sprite) con que genres genera los sprites, y genres, de por sí no nos lo indica. Si no lo has visto antes, abre la carpeta data y échale un ojo a sonic.bmp, ése es el \"spritesheet\" que estamos usando en este tuto. ¡Ah! y recuerda que si vas a usar el tuyo propio: 16 colores y dimensiones múltiplo de ocho.

   
Código: [Seleccionar]
u16 nbTiles = (sonic.height>>3) * (sonic.width>>3);

Si la notación binaria aún no es lo tuyo, no pasa nada. Lo que hemos hecho es tomar el valor en binario del alto (height) del sprite y ventilarle los tres últimos dígitos, lo cual equivale a dividir por ocho de una manera más rápida que haciéndolo al uso. Hemos hecho lo propio con el ancho y luego multiplicado ambos valores. Esto es porque genres guarda tanto el ancho como el ato en píxels, no en tiles, y como ya sabemos, un tile tiene un tamaño de 8x8 píxels.

Si lo de \"sonic\" te extraña y no caes en qué momento hemos creado ese dato, fue antes de meternos con la función main. Así:

   
Código: [Seleccionar]
extern struct genresSprites sonic;

Importando a nuestro código los sprites creados con \"genres\". Justo antes creábamos el struct genresSprites. Vuelve a echarle un vistazo y repasar sus atributos, y si te sobra tiempo y estás de buenas repara en que estamos procediendo igual que en el tuto anterior cuando sólo requeríamos de genres tiles sin más. El atributo \"sprites\" va a entrar en acción en breve. Estate pendiente.

Puñeta anterior realizada, vamos a definir el nuevo sprite usando el método con el que definimos el anterior.

   
Código: [Seleccionar]
SpriteDef spriteErizo;

 Esta vez no usaremos una paleta predefinidia sino que cargamos la paleta del spritesheet. Lo hacemos sobre PAL1. Fácil, ¿no?

   
Código: [Seleccionar]
VDP_setPalette(PAL1, sonic.pal);

Asignamos los que serán los valores iniciales que tome nuestro tercer sprite:

 
Código: [Seleccionar]
 spriteErizo.posx = 128;
    spriteErizo.posy = 128;
    spriteErizo.size = sonic.size>>8;
    spriteErizo.tile_attr = TILE_ATTR_FULL(PAL1,1,1,1,5);
    spriteErizo.link  = 0;  // Como éste va a ser el último sprite que definiremos
// lo hemos de enlazar ya con el primero cuya id es 0.
// Si se te olvida hacer esto no veremos sprite alguno.


Y tercer sprite definido:

   
Código: [Seleccionar]
VDP_setSpriteP(2, &spriteErizo);

Dijimos por ahí atrás que íbamos a animar un sprite frame a frame. Nuestra variable \"frame\" será la que indique cuál frame ha de ser mostrada.

 
Código: [Seleccionar]
 u8 frame = 0;

¡Ah!, ábrete la carpeta \"data\" y déjate a la vista el spritesheet. En el archivo \"resource.rc\" tenemos indicado que cada frame va a ser de 24x32 píxels con lo que cada imagen queda encuadrada debidamente en un área de ese tamaño. Y lo realmente importante es que genres nos lo deja a huevo para acceder a cada frame de forma muy cómoda: usando el atributo \"sprites\" como un array al que especificándole un determinado índice accedemos al frame que se encuentre en dicha posición (comenzando a contar de izquierda a derecha y de arriba a abajo). Por ejemplo, sonic.sprites[6] nos ofrecerá los datos del sexto sprite/frame: el Sonic rojo.


Código: [Seleccionar]
// BUCLE PRINCIPAL ==================================================
    while(1){ // Bucle infinito al canto


Movemos el sprite \"bola2\" incrementando su posición en un píxel ambas coordenadas...

       
Código: [Seleccionar]
spriteBola2.posx++;
        spriteBola2.posy++;


Al usar de nuevo VDP_setSpriteP() pasándole unos valores modificados al sprite 1 (a la \"bola2\") lo que hacemos es actualizar sus propiedades:

Código: [Seleccionar]
       VDP_setSpriteP(1, &spriteBola2);

El sprite de Sonic lo moveremos por pantalla y a la vez que lo vamos a animar cambiando su frame continuamente ...

Escribimos en la posición 5 de la VRAM tantos tiles como correspondan a un frame (nbTiles, la puñeta de más atrás), tomándolos a partir del segundo sprite/frame del spritesheet (sonic.sprites[frame + 1]). Venga, que no es tan complicado. Reléelo unas cuantas veces teniendo a la vista el spritesheet si hace falta.

       
Código: [Seleccionar]
VDP_loadTileData(sonic.sprites[frame + 1], 5, nbTiles, 1);

Incrementamos el frame en cada iteración:

   
Código: [Seleccionar]
    frame++;

Y como los frames corresponidentes a la animación de correr, que son los que nos interesan, con la siguiente instrucción hacemos que cuando la variable frame vaya a tomar el valor 3 \"se resetee\" y vuelva a cero:

       
Código: [Seleccionar]
frame%=3;   // solo tenemos 3 frames, volvemos al primero

Movemos el sprite, modificando su coordenada x.

   
Código: [Seleccionar]
   spriteErizo.posx+=10;

Y registramos los cambios sobre el \"sprite número 2\" que no es otro que nuestro sonic presto a correr de una manera un tanto extraña.

       
Código: [Seleccionar]
VDP_setSpriteP(2, &spriteErizo);

VDP_updateSprites() hace que la consola tome en cuenta los nuevos valores que hayamos podido dar a los sprites. Vamos, que actualiza los sprites...

       
Código: [Seleccionar]
VDP_updateSprites();

 Sincronizamos el dibujo con el retrazo horizontal de pantalla... unas cuantas veces, sí. Es un apaño para procurarnos un retardo conveniente y que nuestros sprites no vayan demasiado lanzados cuando se desplacen. Eres libre de comentar e invalidar tantos como te apetezca (conveniente también es dejar siempre uno al menos).
       
       
Código: [Seleccionar]
VDP_waitVSync();
        VDP_waitVSync();
        VDP_waitVSync();
        VDP_waitVSync();
        VDP_waitVSync();
        VDP_waitVSync();
        VDP_waitVSync();
        VDP_waitVSync();
        VDP_waitVSync();
    }

    return (0); // Return cero, y hasta aquí hemos llegado en esta ocasión.
}


Y con esto y un bizcocho ya sabemos dibujar tiles en las 2 capas y sprites.

Aquí os dejo el fuente del tutorial, recordad cambiar la ruta de vuestro makefile si no es como la mia.

https://dl.dropbox.com/u/33369593/tuto_megadrive/04_sprites.rar



Como deberes... cambiar los tiles de los sprites por algunos vuestros y hacer que se muevan con el pad estaría bien. También dejo el código de la demo que hice hace siglos del SF2, por si quereis cacharrear con ella, deberíais poder entender todo, lo único que no hemos visto es la función para dibujar mapas de tiles usada para dibujar el escenario de la que tratará un futuro tutorial.



https://dl.dropbox.com/u/33369593/tuto_megadrive/sf2.rar

De nuevo agradecer a Bruce las curradas que se mete comentando estos tutos, que sin él tendrian una periodicidad anual por lo menos xD

Deka Black

  • T-600
  • Mensajes: 9 045
No es por nada, pero algo pasa, porque la mayoria de las imágenes que supongo pretenden ilustrar el tutorial son banners de Elotrolado.
A straight line may be the shortest distance between two points, but it is by no means the most interesting. - (Third Doctor in The Time Warrior)

Raiders

  • T-7T
  • Mensajes: 468
Las fotos están cargadas en esa web, y el servidor tendrá capado la muestra de imágenes fuera de su web.

Por cierto, cojonudo el tutorial, currazo que os vais a dar!

pocket_lucho

  • T-500
  • Mensajes: 1 117
Ala, enlaces corregidos, yo es que los veía bien, imagino que por la caché.

En breve la parte 3!

doragasu

  • T-600
  • Mensajes: 2 314
  • Si no está roto, ¡yo lo arreglo!
    • Kernel Hacks
¡Qué gran tuto! ¡Gracias! ¡+1 de Karma para tí!

¡Ojalá tuviese tiempo para cacharrear con mi MD! Una pregunta que creo que ya te hice hace tiempo, a ver si has podido indagar algo... ¿Sabes cómo se puede hacer para usar los puertos de mandos de la Megadrive como salida en lugar de como puerto de joystick estándar? Y creo que también se pueden usar como UART (para transferir datos tipo RS232), ¿sabes algo de esto?

pocket_lucho

  • T-500
  • Mensajes: 1 117

doragasu

  • T-600
  • Mensajes: 2 314
  • Si no está roto, ¡yo lo arreglo!
    • Kernel Hacks
Sí, eso ya me lo pasaste hace tiempo. Viendo el código tal vez pueda imaginarme cómo usar los puertos como salida, pero creo haber leído en alguna parte que también puede usarse como UART, y la verdad estaría bien si así fuera, porque tengo una ideilla rondándome desde hace mucho tiempo.......

EDITO: Aquí es donde lo leí XD, en los detalles de un video de Youtube:
http://www.youtube.com/watch?v=BcwUcTVNSuM

faraday

  • Humano
  • Mensajes: 7
Será mejor

LocoMJ

  • T-7T
  • Mensajes: 299
Estupendo tutorial con todo bien explicadito :D

Habrá que preparar el entorno para cacharrear con el tema y tener el flashcart de mega a mano para darle caña con el hard real :)

realbrucest

  • T-70
  • Mensajes: 204
  • WTC7
    • El emigrante Bruce
Cita de: \"pocket_lucho\" post=27927
En breve la parte 3!

A saber qué megarreforma le andas haciendo al tuto...

Ya sabes que como ahora andas tonteando con el lado oscuro, yo con esas cosas tan feas me lavo las manos. Pero ¿me voy mirando ya el siguiente o a ese \"breve\" todavía le queda? :trollface:

pocket_lucho

  • T-500
  • Mensajes: 1 117
Buff, es que no tengo tiempo ni pa comer xD

Ni idea de para cuando va xDD

pocket_lucho

  • T-500
  • Mensajes: 1 117


pocket_lucho

  • T-500
  • Mensajes: 1 117
Añadido el tuto 4 sobre sprites, además teneis la demo rel sf2 con su fuente para trastear ;)

Jalil

  • Humano
  • Mensajes: 1
mi primer rom, casi mi  :megusta:


[ Guests cannot view attachments ] 12.zip[/attachment]