One of the important aspects when designing a video game is the use of TileSets to generate different levels. This is important because from a collection of graphics, we can generate different levels using the TileMaps.
Up to now we have been working with static images that were displayed as one or several backgrounds. In this chapter, we will work on how to create maps from a TileSet. In addition to being able to use different tools available such as Tiled or using one of the latest versions of SGDK (1.80 or higher).
At the end of this chapter, we will show how to generate different levels both manually and using the rescomp tool integrated in SGDK (remember that you need version 1.80 or higher for some examples).
First of all, let's define properly what are the TileSets and TileMaps; this will also help us to understand how backgrounds are stored and painted in Mega Drive using SGDK.
A TileSet is a set of graphics that compose all the elements that can contain a scenario or the videogame itself. They are usually stored as a bitmap with all the elements.
As we can see in the previous image different graphic elements can be appreciated. This Tileset will allow us to generate scenes with the different elements that compose it.
Now that we have seen what a TileSet is, let's define what a TileMap is. A TileMap is a set of references to the elements of a TileSet to define a scene or any other element needed to display it on screen. Usually we use indexes to indicate the TileSet element to display. Let's see an example of a TileMap of the previous TileSet.
We can see that from the previous Tileset, a new image has been composed in order to generate a scene; in this case a scenario inside a forest. Although it could be used as a static image, in this chapter, we are going to draw it from the information of the TileSet and the information of the TileMap.
It is important to know that when working with a static image in SGDK, it is always composed of a TileSet and a TileMap, so both concepts are being used.
Although different tools can be used to work with TileSets and generate our scenarios, in this book we are going to show a little bit how to use a tool called Tiled.
Tiled is an open source tool, which will allow us to work from Tilesets; we have already talked about it before, but here we are going to see in more detail how to use it to generate from Tilesets our TileMaps to import them to Sega Mega Drive.
As we can see in the previous image, Tiled allows you to create maps from a set of patterns. These sets of patterns and map information can be imported into our Mega Drive game using different tools.
Let's focus on how to create a map using Tiled; specifically from the TileSet example above, we can generate a new map. To do this, we import a new set of patterns to Tiled and save it with the format .tsx; a tsx file stores the information of a TileSet. You can specify the size of each Tile (Remember a Tile is a fragment of an image); in this case, we will use the size of 8x8 px.
When generating a map, we can do it with different layers. Each layer can contain different elements that we have in the different sets of patterns. So that we can enrich our map with more elements.
As we can see in the previous image, we see the two separate layers that when joined together and configured with the transparent color, conform to the previous image; in such a way that the information of both layers can be saved in a TileMap that is generated by the same TileSet.
Both layers can be stored in the same TMX file; the Tmx files store the map information associated with a .tsx file that stores the TileSet information.
For more information about TMX or TSX files, please refer to Tiled's documentation.
But for this case; how can we import this information to our Mega Drive game, there are several ways; but we will use the most current one which is to generate the information, using rescomp within the tools included in SGDK.
However, for this section you will need SGDK 1.80 or higher; so in case you do not have this version, we will show an alternative way.
Another aspect to take into account is that Tiled also allows us to export the information to a Json file (JavaScript Object Notation); which can be useful to import the resources to our game using other tools.
We will start by looking at how to load the TileSet and TileMap information using SGDK's resource management tool, rescomp.
To do this, we can load the information of the two resources, using a .res file; where we will define the information of each resource; let's see an example:
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
We can see in this example, we loaded a palette, a Tileset and two Tilemaps; one for each layer defined in the tmx file.
NOTE: The use of loading the palette can be omitted; as of SGDK version 1.80, it is allowed to load the palette by setting the colors to be used at the beginning of the file. For more information, please consult the SGDK documentation.
Let's see how to define a TileSet:
TILESET name "file" compression optimization
; where:
- name: Resource's name.
- file: Name of the file with the Tileset; it can be an image in bmp,png,tiff format or a tsx file from TILED.
- compression: Compression to be used; it can have the following values:
- -1/BEST/AUTO: Automatic; uses best available compression.
- 0/NONE: No Compression.
- 1/APLIB: Compression using ApLib.
- 2/FAST/LZ4W: Compression using lz4 custom implementation.
- optimization: indicates the optimization to be performed:
- 0/NONE: Does not perform any optimization.
- 1/ALL: Ignore duplicated and mirrored tiles.
- 2/DUPLICATE: Ignore duplicate tiles.
Once we have seen how to import Tilesets, we will see how to import TileMaps; either using images or using a TMX file.
First, we will see how to import a Tilemap from an image:
TILEMAP name "file" tilset_id [compression [map_opt [map_base]]]
; where:
- name: Resource's name.
- file: Name of the image file to be loaded.
- tileset_id: TileMap Identifier.
- compression: Compression to be used; it can have the following values:
- -1/BEST/AUTO: Automatic; uses best available compression.
- 0/NONE: No Compression.
- 1/APLIB: Compression using ApLib.
- 2/FAST/LZ4W: Compression using lz4 custom implementation.
- map_opt: optimization to be performed:
- 0/NONE: No optimization is performed.
- 1/ALL: Ignore duplicated and mirrored tiles.
- 2/DUPLICATE: Ignore duplicate tiles.
- map_base: indicates the base of the tilemap; it is useful to define the priority, palette and tileBase. This is important to be able to load the offset of the tiles to be loaded.
In case of using a TMX file, we can import the resource as follows:
TILEMAP name "file.tmx" "layer" NONE NONE map_base
; where:
- name: Resource's Name.
- file: Name of the TMX map file.
- layer: Name of the layer to load; this is important if you have a tmx file with several layers.
- compression: Compression to be used; it can have the following values:
- -1/BEST/AUTO: Automatic; uses best available compression.
- 0/NONE: No compression.
- 1/APLIB: Compression using ApLib.
- 2/FAST/LZ4W: Compression using lz4 custom implementation.
- map_opt: Optimization to be performed:
- 0/NONE: No optimization is performed.
- 1/ALL: Ignore duplicated and mirrored tiles.
- 2/DUPLICATE: Ignore duplicate tiles.
- map_base: Indicates the base of the tilemap; it is useful to define the priority, palette and tileBase. This is important to be able to load the offset of the tiles to be loaded.
NOTE: If you want to load the Tiles with low priority, you can set the layer name with the suffix "low" or suffix "high".
NOTE2: The priority information can also be loaded by naming the layer with the suffix "priority".
From version 1.90 of SGDK, the possibility of obtaining information of the objects added from Tiled in a TMX using rescomp is available; although it was already added in the previous version, it has been improved in this last version. Let's show the syntax to add it:
OBJECTS name tmx_file layer_id fields_defs [decl_type [type_filter]]
; where:
- name: Resource's name.
- tmx_file: TMX File name.
- layer_id: Layer Identifier.
- field_defs: Definition of fields to be exported.
- decl_type: Type declaration for the objects.
- type_filter: Define a filter for the types to be imported.
For more information on how to import Objects types from a TMX file, see the SGDK documentation.
Having seen how to import resources using TMX or TSX files, let's see how we can import information from a TMX by hand.
If you are using the Visual Studio Code extension; Genesis Code, you can generate an .h file, with the Tilemap information, using the command Genesis Code: Import TMX File; also if you have exported the file as Json format from Tiled, this is also compatible.
To do this, simply in our game, use this command, and select the .tmx file, it will automatically generate an .h file.
For more information on how to import Tmx files, using Genesis Code, see the Genesis Code documentation.
We can also obtain the information manually; to do so, we can open the TMX file with a text editor and obtain the information (only if the information is as CSV 1 and uncompressed).
We can see in the previous image, that the information is in the data tag and that it must be coded as csv; we will copy the complete list of indexes, and we will create a .h file in the inc folder; there we will save the information in the following format:
u16 map[1120]=481,482,483,484,485,482.....;
It is important to know the number of Tiles, which must be the same of your map.
Of course this process could be automated using different scripts or tools. But in this case it is important to know how to import this information.
Once we have seen how to import TileSets and TileMaps to our game, let's see how to use them and display them on the screen; so that it will be easier to show the different scenes of our game.
In this case, we are going to show two examples; to be able to import and use the resources using a TMX file; or directly with the information stored in a .h file.
We will start with the example using a TMX file; remember that this version will only work if you are using SGDK version 1.80 or higher. This example named ej9.tilesets1 can be found in the example repository that accompanies this book.
In this example, we are going to load a Tileset, and then the corresponding TileMaps; which we have imported using rescomp; with the following configuration:
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
Translation Note: Some of these resources have Spanish names; but the functionality should be the same.
We can see that we have a palette, which will store the information of the colors, a Tileset, with the information of the Tileset to load, that we see that it has no compression or optimization. In addition we load 2 TileMaps; which correspond to the same TMX file, but two different layers so that we will load different layers from this file. We can observe that we define the Base Tile to 16 that indicates the offset to have when loading the information of the Tiles ( which corresponds to the initial value of the VRAM user space).
Let's see the source code of this example:
#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);
}
We can see in the example that different functions are used to load both the TileSet and the TileMaps; let's take a look at these functions:
The first function that we see is VDP_loadTileSet
which loads the information of a TileSet in VRAM; so it is available to load the TileMaps; let's see the parameters of the function:
- tileset*: Pointer where the Tileset resource to be loaded is located.
- tileIndex: Index of the tile to be used as a base. You can use the index
TILE_USER_INDEX
to use the first available position. - tm: transfer method; it allows using the CPU, or the different DMA values; it can have the following values:
- CPU: CPU is used.
- DMA: DMA is used.
- DMA_QUEUE: DMA Queue is used.
- DMA_QUEUE_COPY: DMA Copy Queue is used.
NOTE: If the Tileset resource is compressed, it is first decompressed in memory.
In our example DMA is used to transfer the information; remember not to overload the DMA as it shares the bus with the CPU and there could be bottlenecks.
On the other hand, let's see how to load the Tilemap information, using the function VDP_setTileMap
; this function allows to load the tilemap information, the palette information and priority, it will load it from the mapbase defined with rescomp; let's see the parameters of this function:
- Plane: Indicates the plane to be used; it can be either
BG_A
orBG_B
. - tilemap*: Pointer to the resource where this TileMap is stored.
- x: X Map's position in Tiles.
- y: Y Map's position in Tiles.
- w: Map's Width in Tiles.
- h: Map's height in Tiles.
- tm: transfer method; it allows using the CPU, or the different DMA values; it can have the following values:
- CPU: CPU is used.
- DMA: DMA is used.
- DMA_QUEUE: DMA Queue is used.
- DMA_QUEUE_COPY: DMA Copy Queue is used.
There are variants of this function that load the palette or priority information, such as VDP_setTileMapEx
, with which you can specify the mapbase. You can see how to use this function in the SGDK documentation.
Once we have this example ready, we can compile and run to see how the background is loaded:
Example 9: use of Tilesets using TMX File
If we cannot use a TMX file, we can load the information manually; creating by hand the .h file (or using the extension Genesis Code); so that we can load the information in our game and display it on the screen. This example, called ej10.tileset2, can be found in the example repository that accompanies this book.
First, let's show how we will import the information in this example; first the Tileset and the palette information will be imported using rescomp:
PALETTE palbosque "tilesetbosque.png"
TILESET tilesetBosque "tilesetbosque.png" 0
It is also important that at the time of loading the Tileset the optimization parameter is not activated in order not to have problems when loading the tilemap indexes. Once imported these resources with rescomp, we can create a .h file, with the information obtained from the TMX file.
u16 map1[1120]={481,482,483,484,...};
With this information, we can store each Tile in an array position and then paint it on the screen.
Let's see a fragment:
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);
We observe how two arrays of type u16 have been defined, containing 1120 positions; one for each Tile to be stored; then these Tiles are filled, using the information stored in the .h file, and using the macro TILE_ATTR_FULL
to load in each Tile the information of the palette, priority, etc.
After loading each Tile, we draw on screen each layer, using the function VDP_setTileMapDataRect
.
This function, displays a tilemap as a rectangle; so we can draw screen areas, with the stored Tiles; it can receive the following parameters:
- Plane: Plane to be drawn can be
BG_A
orBG_B
. - data: Pointer to the first position where the data is located.
- x: Beginning X Position (In tiles).
- y: Beginning Y Position (In tiles).
- w: Width to draw in tiles.
- h: Stop drawing in tiles.
- wm: Width of the source Tilemap to draw in Tiles.
- tm: transfer method; it allows using the CPU, or the different DMA values; it can have the following values:
- CPU: CPU is used.
- DMA: DMA is used.
- DMA_QUEUE: DMA Queue is used.
- DMA_QUEUE_COPY: DMA Copy Queue is used.
We see that to calculate the Tile to show, we use a formula that is to search in the array that we have created in the .h file, and then all the tiles are displayed on the screen. There are other functions to make this data as VDP_setTileMapXY
that allows drawing in a concrete coordinate; for more information about how to use these functions, you can see the SGDK documentation.
Once we have finished reviewing the code, we can compile and run this example:
Example 10: Using TileSets manually
As we can see, the result is almost the same (it can change by the way of loading the palette); and we have already seen how to use TileSets and TileMaps in Sega Mega Drive, using external tools such as Tiled, and how SGDK version 1.80 or higher allows to load TMX format files made with the same tool.
- Example TileSet (Open Game Art): https://opengameart.org/content/forest-tileset-new-and-old.
- Tiled: https://www.mapeditor.org/.
- Genesis Code Documentation: https://zerasul.github.io/genesis-code-docs/
Footnotes
-
CSV (Comma Separated Values); file format that stores each data separated by ",". ↩