Thank you to my excellent patron Daniel, who suggested this tutorial!
Internally, the Mega Drive splits all graphics up into 8x8 pixel chunks called tiles. These tiles are especially important when working with backgrounds. So, let’s take a look at the various ways SGDK lets you place tiles on the screen!
Placing a single Tile
The easiest thing you can do is to place a single tile. And, as expected, this is very easy to achieve in SGDK. Simply call the following function:
void VDP_setTileMapXY(VDPPlane plane, u16 tile, u16 x, u16 y);
Just pass in the plane you want to draw the tile on, the index of the tile itself, and where to draw it. Note that the coordinates are given in tiles, not pixels! So an
x value of
1 would draw the tile 8 pixels from the left edge of the screen.
Filling an Area With the Same Tile
If you want to fill an area with copies of the same tile, you could of course write a loop and just use the function for placing single tiles mentioned above. But, there is also an easier way:
void VDP_fillTileMapRect(VDPPlane plane, u16 tile, u16 x, u16 y, u16 w, u16 h);
As the name might imply, this function fills a rectangular area with tiles. You pass in the plane you want to draw the tiles on, the index of the tile, the coordinates of the top-left corner of the desired rectangle, as well as the width and height of the rectangle. Again, these coordinates are all given in tiles, not pixels.
Drawing Multiple Tiles
But what if you want to draw a bigger image that’s spread across multiple tiles? SGDK comes with a function for that too! This function looks like this:
void VDP_fillTileMapRectInc(VDPPlane plane, u16 basetile, u16 x, u16 y, u16 w, u16 h);
It looks like an expanded version of
VDP_fillTileMapRect, and that’s because it is. Just like
VDP_fillTileMapRect, it draws a rectangle of tiles on the screen, but there is one important difference. See how the second parameter is called
basetile, not just
tile? That’s because we’re only specifying the first tile to be drawn, not the only tile. What this means is: The function calculates how many tiles it needs to draw to create the rectangle, then draws one tile after the other, starting with the tile specified in
basetile. So, if you’re drawing a 4x4 square and specify
1 as the basetile, the function will draw these tiles like this:
This is useful when you have background objects that are bigger than one tile, such as the lamp posts in the Megarunner tutorial.
Drawing Whole Images
Finally: What if you want to draw a big image, maybe one that’ll even fill the whole screen? You could assemble it using all the previous functions, but that can be tricky in certain cases. First, you’ll obviously have to keep track of all the parts and tile indices yourself. But, there’s also another catch: By default, SGDK’s resource compiler ignores duplicate tiles during import. This means that if your image has duplicate tiles—such as identical blue sky tiles—then only one instance of these tiles will be imported. And this, in turn, spells trouble for a function like
VDP_fillTileMapRectInc, which requires all its tiles to be neatly lined up in a row!
A workaround would be to tell SGDK to import all tiles, even the duplicates, but this will quickly blow up your VRAM with countless copies of identical tiles. So, instead, just use the following function:
bool VDP_setTileMapEx(VDPPlane plane, const TileMap *tilemap, u16 basetile, u16 xp, u16 yp, u16 x, u16 y, u16 w, u16 h, TransferMethod tm);
Yikes, those are a lot of parameters. The first one is obviously the plane you want to draw your image on. The last one specifies the transfer method, which you can set to
DMA_QUEUE or something else (I’ll eventually cover these in a different tutorial). The
*tilemap parameter takes a tilemap.
What is that and where do you get it?
A tilemap in SGDK is a data structure that is automatically generated when importing large images of type
IMAGE. SGDK splits big images up into tiles, and then generates a tilemap that specifies which tile goes where. This is great, because this automatically takes duplicate tiles into account! To access the tilemap property of an imported resource, just use
myimage.tilemap, just like you would use
myimage.palette to access the palette data.
After passing in the tilemap, you pass in the
basetile, which is the index of the first tile of your image in VRAM.
Now, the coordinate parameters can be a bit confusing.
yp specify where your image will be drawn, while
height specify the region of your image to be drawn. This allows you to only draw certain parts of your image to the screen. All of these values are given in tiles, not pixels. So, for example, if you have an image that’s 32x32 tiles big, and you only wanted to draw the top-right quarter of it, you’d pass in
And that’s it! Those are the four functions that let you fill the screen with beautiful tiles in multiple ways. There are a few more functions for more specialized cases, but these are the basics that should be enough most of the time.
If you've got problems or questions, join the official SGDK Discord! It's full of people a lot smarter and skilled than me. Of course you're also welcome to just hang out and have fun!
Take It to the Next Level!Become an excellent patron on Patreon and snatch yourself some kickass perks such as early access, early builds, exclusive updates and more!
You will also be added to the Wall of Excellent People!
Check out the rest of this tutorial series!