Skip to content

Latest commit

 

History

History
340 lines (238 loc) · 20.6 KB

TileSets.md

File metadata and controls

340 lines (238 loc) · 20.6 KB

12. TileSets

Uno de los aspectos importantes a la hora de diseñar un videojuego es el uso de TileSets para generar distintos niveles. Esto es importante ya que a partir de una colección de gráficos, podemos generar distintos niveles usando los llamados TileMaps.

Hasta ahora hemos estado trabajando con imágenes estáticas que se mostraban como uno o varios fondos. En este capítulo, trabajaremos la forma de poder crear mapas a partir de un TileSet. Además de poder utilizar distintas herramientas disponibles como puede ser Tiled o usar una de las últimas versiones de SGDK (1.80 o superior).

Al final de este capítulo, mostraremos cómo se puede generar distintos niveles tanto de forma manual, como usando la herramienta rescomp que integra SGDK (Recuerda que necesitas la versión 1.80 o superior para algunos ejemplos).

Tilesets y TileMaps

En primer lugar, vamos a definir correctamente qué son los llamados TileSets y TileMaps; nos ayudará a comprender cómo se almacenan y se pintan los fondos en Mega Drive usando SGDK.

Un TileSet o conjunto de patrones, es un conjunto de gráficos que componen todos los elementos que puede contener un escenario o el propio videojuego. Se suelen almacenar como un mapa de bits con todos los elementos.

Ejemplo de TileSet (Open Game Art) Ejemplo de TileSet (Fuente: Open Game Art)

Como podemos ver en la imagen anterior, se pueden observar los distintos elementos gráficos. Este Tileset nos va a permitir generar escenarios con los distintos elementos que lo componen.

Una vez hemos visto lo que es un TileSet, definiremos el llamado TileMap. Un TileMap es un conjunto de referencias a elementos de un TileSet para definir un escenario o cualquier otro elemento necesario para mostrarlo en pantalla. Normalmente se utilizan índices para indicar el elemento del TileSet a mostrar. Veamos un ejemplo de un TileMap a partir del Tileset anterior.

Ejemplo TileMap Ejemplo TileMap

Observamos que a partir del anterior Tileset, se ha compuesto una nueva imagen para poder generar un escenario; en este caso una escena dentro de un bosque. Aunque se podría utilizar como una imagen estática, en este capítulo vamos a dibujarla a partir de la información del TileSet y la información del TileMap.

Es importante conocer que a la hora de trabajar con una imagen estática en SGDK, esta se compone siempre de un TileSet y de un TileMap por lo que se están utilizando ambos conceptos.

Aunque se pueden utilizar distintas herramientas para trabajar con TileSets y generar nuestros escenarios, en este libro vamos a mostrar un poco cómo utilizar una herramienta llamada Tiled.

Tiled

Tiled es una herramienta de código abierto, que nos va a permitir trabajar a partir de Tilesets (o conjuntos de patrones); aunque ya hemos hablado de ella anteriormente, aquí vamos a ver más en detalle cómo utilizar esta herramienta para generar a partir de Tilesets, nuestros TileMaps para importarlos a Sega Mega Drive.

Tiled Tiled

Como podemos ver en la anterior imagen, Tiled permite crear mapas a partir de un conjunto de patrones. Estos conjuntos de patrones e información del mapa, se pueden importar a nuestro juego de Mega Drive usando distintas herramientas.

Vamos a centrarnos en cómo crear un mapa usando Tiled; concretamente a partir del ejemplo de TileSet anterior, podamos generar un nuevo mapa. Para ello, se importa un nuevo conjunto de patrones, a Tiled y se guarda con el formato .tsx; un fichero tsx, almacena la información de un conjunto de patrones. Se le puede indicar el tamaño de cada Tile (Un Tile recuerda que es un fragmento de la imagen); en este caso, usaremos el tamaño de 8x8 px.

A la hora de generar un mapa, podemos hacerlo con distintas capas. Cada capa puede contener distintos elementos que tengamos en los distintos conjuntos de patrones. De tal forma que podamos enriquecer nuestro mapa con más elementos.

Capas Capas

Como podemos ver en la imagen anterior, vemos las dos capas separadas que al juntarlas y configurar el color transparente; se conforman la imagen anterior; de tal forma que la información de ambas capas se puede guardar en un TileMap que es generado por el mismo TileSet.

Ambas capas se pueden almacenar en el mismo fichero TMX; los ficheros Tmx almacenan la información de los mapas asociados a un fichero .tsx que almacena la información de los TileSet.

Para más información acerca de los ficheros TMX o TSX, puede consultar la documentación de Tiled.

Pero para nuestro caso; como podemos importar esta información a nuestro juego de Mega Drive, existen varias formas; pero utilizaremos la más actual; que se trata de generar la información, usando rescomp dentro de las herramientas que incluye SGDK.

Sin embargo, para este apartado necesitarás SGDK 1.80 o superior; por lo que en caso de no tener esta versión, mostraremos una forma alternativa.

Otro aspecto a tener en cuenta, es que Tiled también permite exportar la información a un fichero Json (JavaScript Object Notation); el cual puede ser útil para importar los recursos a nuestro juego usando otras herramientas.

Generar TileMap con Rescomp

Comenzaremos a ver como se puede cargar la información tanto del TileSet, como del TileMap, usando la herramienta de gestión de recursos de SGDK, rescomp.

Para ello, podemos cargar la información de los dos recursos usando un fichero .res; donde definiremos la información de cada recurso; veamos un ejemplo:

PALETTE palbosque   "tilesetbosque.png"
TILESET mapabosque  "tilesetbosque.tsx" NONE NONE
TILEMAP map1  "mapabosque.tmx" "Capa de patrones 1" NONE NONE 16
TILEMAP map1b  "mapabosque.tmx" "Capa de patrones 2" NONE NONE 16

Podemos ver que en este ejemplo, cargamos una paleta, un Tileset y dos Tilemap; uno por cada capa definida en el fichero tmx.

NOTA: Puede obviarse el uso de cargar la paleta; ya que a partir de la versión 1.80 de SGDK, se permite cargar la paleta estableciendo al inicio del fichero los colores a utilizar. Para más información, por favor consultar la documentación de SGDK.

Veamos como se define un TileSet:

TILESET name "file" compression optimization ; donde:

  • name: nombre del recurso
  • file: nombre del fichero con el Tileset; puede ser una imagen en formato bmp,png,tiff o un fichero .tsx de TILED.
  • compression: Compresión a utilizar; puede tener los siguientes valores:
    • -1/BEST/AUTO: automática; utiliza la mejor compresión disponible.
    • 0/NONE: ninguna compresión.
    • 1/APLIB: compresión usando ApLib.
    • 2/FAST/LZ4W: compresión usando la implementación personalizada de lz4.
  • optimization: indica la optimización a realizar:
    • 0/NONE: No realiza ninguna optimización.
    • 1/ALL: Ignora los tiles duplicados y espejados.
    • 2/DUPLICATE: Ignora los tiles duplicados.

Una vez visto cómo se importan los Tilesets, pasaremos a ver cómo se importan los TileMaps; tanto usando imágenes, como utilizando un fichero TMX.

En primer lugar, veremos cómo se importa un Tilemap a partir de una imagen:

TILEMAP name "file" tilset_id [compression [map_opt [map_base]]] ; donde:

  • name: nombre del recurso.
  • file: nombre del fichero de la imagen a cargar.
  • tileset_id: identificador que tendrá este Tilemap.
  • compression: Compresión a utilizar; puede tener los siguientes valores:
    • -1/BEST/AUTO: automática; utiliza la mejor compresión disponible.
    • 0/NONE: ninguna compresión.
    • 1/APLIB: compresión usando ApLib.
    • 2/FAST/LZ4W: compresión usando la implementación personalizada de lz4.
  • map_opt: indica la optimización a realizar:
    • 0/NONE: No realiza ninguna optimización.
    • 1/ALL: Ignora los tiles duplicados y espejados.
    • 2/DUPLICATE: Ignora los tiles duplicados.
  • map_base: indica la base del Tilemap; es útil para definir la prioridad, paleta y tileBase. Este es importante para poder cargar el offset de los tiles a cargar.

En el caso de utilizar un fichero TMX, podemos importar el recurso de la siguiente forma:

TILEMAP name "file.tmx" "layer" NONE NONE map_base; donde:

  • name: nombre del recurso.
  • file: nombre del fichero TMX del mapa.
  • layer: nombre de la capa a cargar; esto es importante si se tiene un fichero tmx con varias capas.
  • compression: Compresión a utilizar; puede tener los siguientes valores:
    • -1/BEST/AUTO: automática; utiliza la mejor compresión disponible.
    • 0/NONE: ninguna compresión.
    • 1/APLIB: compresión usando ApLib.
    • 2/FAST/LZ4W: compresión usando la implementación personalizada de lz4.
  • map_opt: indica la optimización a realizar:
    • 0/NONE: No realiza ninguna optimización.
    • 1/ALL: Ignora los tiles duplicados y espejados.
    • 2/DUPLICATE: Ignora los Tiles duplicados.
  • map_base: indica la base del Tilemap; es útil para definir la prioridad, paleta y tileBase. Este es importante para poder cargar el offset de los tiles a cargar.

NOTA: Si se quieren cargar los Tiles con baja o alta prioridad, se puede establecer el nombre de la capa con el sufijo "low" o sufijo "high".

NOTA 2: También puede realizarse la carga de la información de la prioridad nombrando la capa con el sufijo "priority".

A partir de la versión 1.90 de SGDK, se dispone la posibilidad de obtener información de los objetos añadidos desde Tiled en un TMX usando rescomp; aunque ya lo añadía en la anterior versión, se ha mejorado en esta última versión. Vamos a mostrar la sintaxis para añadirlo:

OBJECTS name tmx_file layer_id fields_defs [decl_type [type_filter]]; donde:

  • name: Nombre del recurso.
  • tmx_file: fichero TMX.
  • layer_id: Identificador de la capa.
  • field_defs: Definición de los campos a exportar.
  • decl_type: Declaración de tipo para los objetos.
  • type_filter: Define un filtrado para los tipos que se quieran importar.

Para más información acerca de cómo se importan los tipos Objects desde un fichero TMX, consulta la documentación de SGDK.

Generar TileMap a mano

Una vez visto cómo importar los recursos usando ficheros TMX o TSX; vamos a ver cómo podemos importar la información de un TMX, a mano.

Si estás utilizando la extensión para Visual Studio Code; Genesis Code, puedes generar un fichero .h, con la información del Tilemap con el comando Genesis Code: Import TMX File; además si desde Tiled; has exportado el fichero como formato Json, también es compatible.

Para ello, simplemente en Visual Studio Code utilizar dicho comando, y seleccionar el fichero .tmx automáticamente se generará un fichero .h.

Para más información de como importar ficheros Tmx, usando Genesis Code, consulta la documentación de dicha extensión.

También podemos obtener la información a mano; para ello, podemos abrir el fichero TMX con un editor de texto y obtener la información (Sólo si la información esta como CSV 1 y sin comprimir).

Fichero TMX Fichero TMX

Podemos ver en la anterior imagen, que la información se encuentra en la etiqueta data y que debe estar codificada como csv; copiaremos la lista completa de índices, y crearemos un fichero .h en la carpeta inc; allí guardaremos la información de la siguiente forma:

u16 map[1120]=481,482,483,484,485,482.....;

Es importante saber el nº de Tiles que debe ser igual que el de nuestro mapa.

Obviamente este proceso se podría automatizar utilizando distintos scripts o herramientas. Pero en este caso es importante conocer cómo importar esta información.

Ejemplos con TileSets

Una vez hemos visto cómo se importan los TileSets y TileMaps a nuestro juego, vamos a ver cómo utilizarlos y mostrarlos por pantalla; de tal forma que sea más fácil mostrar los distintos escenarios de nuestro juego.

En este caso, vamos a mostrar dos ejemplos; para poder importar y utilizar los recursos usando un fichero TMX; o directamente con la información almacenada en un fichero .h.

Fichero TMX

Comenzaremos con el ejemplo utilizando un fichero TMX; recuerda que esta versión solo funcionará si usas la versión de SGDK 1.80 o superior. Este ejemplo llamado ej9.tilesets1 se encuentra en el repositorio de ejemplos que acompaña a este libro.

En este ejemplo, vamos a cargar un Tileset y luego los correspondientes TileMaps; que hemos importado usando rescomp; con la siguiente configuración:

PALETTE palbosque   "tilesetbosque.png"
TILESET mapabosque  "tilesetbosque.tsx" NONE NONE
TILEMAP map1  "mapabosque.tmx" "Capa de patrones 1" NONE NONE 16
TILEMAP map1b  "mapabosque.tmx" "Capa de patrones 2" NONE NONE 16

Podemos ver que tenemos una paleta, que almacenará la información de los colores, un Tileset, con la información del tileset a cargar, que vemos que no tiene compresión ni optimización. Además que cargamos 2 TileMaps; los cuales corresponden al mismo fichero TMX, pero a distintas capas de tal forma que cargaremos distintas capas en este fichero. Podemos observar que definimos el Tile Base a 16 que indica el offset a tener a la hora de cargar la información de los Tiles (que corresponde al valor inicial del espacio de VRAM para el usuario).

Veamos el código fuente de este ejemplo:

#include <genesis.h>

#include "tileset.h"

int main()
{

    u16 ind = TILE_USER_INDEX;
    VDP_loadTileSet(&mapabosque, ind, DMA);
    PAL_setPalette(PAL0,palbosque.data,DMA);
    VDP_setTileMap(BG_B,&map1,
    0,0,map1.w,map1.h,DMA);
    VDP_setTileMap(BG_A,&map1b,
    0,0,map1b.w,map1b.h,DMA);
    while(1)
    {
        SYS_doVBlankProcess();
    }
    return (0);
}

Podemos ver en el ejemplo, que se utilizan distintas funciones para cargar tanto el TileSet, como los TileMap; vamos a ver esas funciones:

La primera función que observamos es VDP_loadTileSet la cual carga la información de un TileSet en VRAM; de tal forma que quede disponible. para cargar los TileMaps; veamos los parámetros de la función:

  • tileset*: Puntero donde se encuentra el recurso del Tileset a cargar.
  • tileIndex: Índice del tile a utilizar como base. Puede usarse como índice TILE_USER_INDEX para que use la primera posición disponible.
  • tm: método de transferencia; permite utilizar la CPU, o los distintos valores de DMA; puede tener los siguientes valores:
    • CPU: se utilizará la CPU.
    • DMA: se utilizará DMA.
    • DMA_QUEUE: Se utilizará la cola de DMA.
    • DMA_QUEUE_COPY: Se utilizará cola de DMA como copia.

NOTA: Si el recurso Tileset está comprimido, primero se descomprime en memoria.

En nuestro ejemplo se utiliza DMA para transferir la información; recuerda no sobrecargar el DMA ya que comparte bus con la CPU y podría haber cuellos de botella.

Por otro lado, vamos a ver como se carga la información de los Tilemap, usando la función VDP_setTileMap; esta función permite cargar la información del tilemap, la información de la paleta y prioridad. Se cargará del mapbase definido con rescomp; veamos los parámetros de esta función:

  • Plano: Indica el plano a utilizar; puede ser BG_Ao BG_B.
  • tilemap*: Puntero al recurso donde se almacena este TileMap.
  • x: posición x del mapa en Tiles.
  • y: posición y del mapa en Tiles.
  • w: ancho en Tiles.
  • h: alto en Tiles.
  • tm: método de transferencia; permite utilizar la CPU, o los distintos valores de DMA; puede tener los siguientes valores:
    • CPU: se utilizará la CPU.
    • DMA: se utilizará DMA.
    • DMA_QUEUE: Se utilizará la cola de DMA.
    • DMA_QUEUE_COPY: Se utilizará cola de DMA como copia.

Existen variantes de esta función que cargan la información de la paleta o prioridad como VDP_setTileMapEx, que se le puede indicar el mapbase. Puedes ver como utilizar esta función, en la documentación de SGDK.

Una vez tenemos este ejemplo listo, ya podemos compilar y ejecutar para ver cómo se carga el fondo:

Ejemplo 9: Uso de TileSets usando fichero TMX Ejemplo 9: Uso de TileSets usando fichero TMX

Ejemplo a mano

Si por el contrario no se puede utilizar un fichero TMX, podemos cargar a mano la información; creando un fichero .h (o utilizando la extensión Genesis Code); de tal forma que podamos cargar dicha información en nuestro juego y mostrarlo por pantalla. Este ejemplo llamado ej10.tileset2, puedes encontrarlo en el repositorio de ejemplos que acompaña a este libro.

En primer lugar, vamos a mostrar cómo importamos en este ejemplo la información; primero el Tileset y la información de la paleta que serán importados usando rescomp:

PALETTE palbosque "tilesetbosque.png"
TILESET tilesetBosque "tilesetbosque.png" 0

Es importante que a la hora de cargar el Tileset el parámetro de optimización no este activado para no tener problemas a la hora de cargar los índices del tilemap. Una vez importados estos recursos con rescomp, ya podemos crear un fichero .h, con la información obtenida del fichero TMX.

u16 map1[1120]={481,482,483,484,...};

Con esta información, podemos almacenar cada Tile en una posición de un array para después pintarla en pantalla.

Veamos un fragmento:

    u16 ind = TILE_USER_INDEX;
    VDP_loadTileSet(&tilesetBosque,
        TILE_USER_INDEX,CPU);
    PAL_setPalette(PAL0,palbosque.data,CPU);
    int i,j;
    u16 tileMap1[1120];
    u16 tileMap1b[1120];
    for (i = 0; i < 40; i++)
        {
        for (j = 0; j < 28; j++)
         {
             tileMap1[(i) + 40 * j]=
                TILE_ATTR_FULL(PAL0, FALSE,
                 FALSE, FALSE, 
                (ind-1) + map1b[(i) + 40 * j]);
             tileMap1b[(i) + 40 * j]=
                TILE_ATTR_FULL(PAL0, FALSE,
                 FALSE, FALSE,
                 (ind-1) + map1[(i) + 40 * j]);
         }
        }
    VDP_setTileMapDataRect(
        BG_A,tileMap1,0,0,40,28,40,CPU);
    VDP_setTileMapDataRect(
        BG_B,tileMap1b,0,0,40,28,40,CPU);

Observamos cómo se han definido dos arrays de tipo u16, que contienen 1120 posiciones; uno por cada Tile a almacenar; seguidamente se rellenan esos Tiles usando la información almacenada en el fichero .h; y usando la macro TILE_ATTR_FULL para ir cargando en cada Tile la información de la paleta, prioridad,etc.

Después de cargar cada Tile, dibujamos por pantalla cada capa, usando la función VDP_setTileMapDataRect.

La función VDP_setTileMapDataRect, muestra por pantalla un Tilemap como un rectángulo; por lo que podemos dibujar áreas de pantalla con los Tiles almacenados; puede recibir los siguientes parámetros:

  • Plane: Plano a dibujar puede ser BG_A o BG_B.
  • data: Puntero a la primera posición donde se encuentran los datos.
  • x: Posición x donde comenzar (En tiles).
  • y: Posición y donde comenzar (En tiles).
  • w: Ancho a dibujar en tiles.
  • h: Alto a dibujar en tiles.
  • wm: Ancho del Tilemap de origen a dibujar en Tiles.
  • tm: método de transferencia; permite utilizar la CPU, o los distintos valores de DMA; puede tener los siguientes valores:
    • CPU: se utilizará la CPU.
    • DMA: se utilizará DMA.
    • DMA_QUEUE: Se utilizará la cola de DMA.
    • DMA_QUEUE_COPY: Se utilizará cola de DMA como copia.

Vemos que para calcular el Tile a mostrar, usamos una fórmula que se trata de ir buscando en el array que hemos creado en el fichero .h, y posteriormente se muestran todos los tiles por pantalla. Existen otras funciones para realizar estos datos como VDP_setTileMapXY, que permite dibujar en una coordenada en concreto; para más información acerca de como usar estas funciones, puedes consultar la documentación de SGDK.

Una vez que hemos terminado de revisar el código, ya podemos compilar y ejecutar este ejemplo:

Ejemplo 10: Uso de TileSets a mano Ejemplo 10: Uso de TileSets a mano

Como podemos ver, el resultado es casi el mismo (puede cambiar por la forma de cargar la paleta); y ya hemos podido ver como utilizar TileSets y TileMaps en Sega Mega Drive; utilizando herramientas externas como pueden ser Tiled, y como la versión de SGDK 1.80 o superior permite cargar ficheros con formato TMX realizados con la misma herramienta.

Referencias

Footnotes

  1. CSV (Comma Separated Values); formato de fichero que almacena cada dato separado por ",".