Build Your Own Game Boy Game, Part 3: Drawing Your World
- Marcel Pflug
- Apr 22
- 4 min read
Updated: Jul 4
We now know that the Game Boy draws everything from small 8 by 8 tiles placed on a grid. In this part we will design our own tiles, load them into the console and use them to draw the playfield for Snake, including a solid wall around the edge of the screen. By the end you will have a real game board sitting on that green screen, ready for a snake to move across.

Designing Your Own Game Boy Tiles
A tile is 8 by 8 pixels, and each pixel is one of four shades. In code, a single tile is described by 16 numbers. For our game we only need four tiles: an empty space, the snake's body, a piece of food and a wall. Here is how we describe them. Do not worry about every individual number; the comments tell you which tile is which, and you can tweak them freely later.
#include <gb/gb.h>
#include <stdint.h>
// tile numbers, so we can use names instead of bare numbers
#define T_EMPTY 0
#define T_BODY 1
#define T_FOOD 2
#define T_WALL 3
// 16 bytes per tile: empty, body (solid), food (diamond), wall (solid)
const uint8_t tiles[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x18,0x18,0x3C,0x3C,0x7E,0x7E,
0x7E,0x7E,0x3C,0x3C,0x18,0x18,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
};Here is how to read that block. The two include lines at the top bring in the Game Boy toolkit and the number types we use. The four define lines just give friendly names to the tile slots, so we can write T_FOOD instead of the bare number 2. Then the tiles array holds the actual pictures: sixteen numbers per tile, two for each of the eight rows. A row of 0x00 is completely empty, a row of 0xFF is fully solid (that is how the body and the wall become solid dark blocks), and the food's numbers grow then shrink (0x18, 0x3C, 0x7E, then back down) to trace a diamond. If you would like to see exactly how a row of pixels turns into a number like 0x7E, we walk through that arithmetic step by step in the Level Up series, Part 1.
Giving the tiles names with those define lines is a small habit that pays off. Writing T_FOOD later in the code is far clearer than writing the number 2, and if you ever reorder your tiles you only change one line.
Loading the Tiles and Turning On the Screen
Designing tiles is not enough; we have to copy them into the console's video memory and switch the display on. GBDK-2020 makes this a three-line affair at the start of our program. The first line loads our four tiles, the second tells the Game Boy to show the background layer, and the third turns the screen on.
set_bkg_data(0, 4, tiles); // load 4 tiles, starting at slot 0
SHOW_BKG; // show the background layer
DISPLAY_ON; // turn the screen onThose three lines are the switch-on sequence. set_bkg_data(0, 4, tiles) copies our artwork into the console's video memory: the 0 means start at tile slot 0, the 4 is how many tiles to load, and tiles is where to copy them from. SHOW_BKG and DISPLAY_ON are simply on switches, the first for the background layer and the second for the screen itself. Run only this much and you would see a blank, lit screen, ready to be drawn on.
Drawing the Walls
Our playfield is the whole screen, which is 20 tiles wide and 18 tiles tall. We want a solid wall running all the way around the edge, so the snake has something to crash into. We will define the size of the grid as names, then write a small function that places wall tiles along the top, bottom, left and right edges.
#define GRID_W 20 // the background is 20 tiles wide
#define GRID_H 18 // and 18 tiles tall
void draw_walls(void) {
uint8_t x, y;
for (x = 0; x < GRID_W; x++) {
set_bkg_tile_xy(x, 0, T_WALL); // top edge
set_bkg_tile_xy(x, GRID_H - 1, T_WALL); // bottom edge
}
for (y = 0; y < GRID_H; y++) {
set_bkg_tile_xy(0, y, T_WALL); // left edge
set_bkg_tile_xy(GRID_W - 1, y, T_WALL); // right edge
}
}If you have not met a for loop before, it is simply a way to repeat something a set number of times. Read for (x = 0; x < GRID_W; x++) as: start with x at 0, keep going as long as x is less than the width, and add one to x each time round. So the first loop runs x across every column, dropping a wall tile on the top row (row 0) and the bottom row each time; the second loop runs y down every row, placing a wall on the left column (column 0) and the right column. Four edges, drawn by two little loops.
Read that function slowly and it is quite friendly. The first loop walks across every column and drops a wall tile on the very top row and the very bottom row. The second loop walks down every row and places a wall tile on the far left and far right columns. Together they frame the screen with a tidy border.
Seeing It On Screen
If you call draw_walls right after loading the tiles and turning on the display, then build the ROM the same way as in Part 1, your emulator will show a clean rectangular arena framed by a solid border. It may not look like much yet, but you are now drawing your own graphics, pixel patterns you designed yourself, on real Game Boy hardware. That is a genuine milestone.

Coming Up in Part 4
We have an arena, but nothing moves. In Part 4 we will bring the snake to life: we will store its body in memory, read the player's button presses, and build the game loop that makes it slither around the board one step at a time. This is where it finally starts to feel like a game.









Comments