As was covered in Part 1, the Game Boy uses two main sections of graphical memory: sprites and background. In order to place a tile for the background, it must be in the background. In order to place a tile for a sprite, it must be in the sprite memory. (There are ways to read and write directly to the different memory locations using GBDK functions and effectively swap things, but that’s a much more advanced topic.)
For our example of using background tiles, we will be printing the classic words “Hello world.” However, to do that, we need tiles to actually write.
Download or copy the content of “alpha.c” into a file named exactly that: alpha.c.
Note: As was mentioned in Part 1, tiles are represented as collections of hexadecimal numbers. The array alpha is 47 tiles representing numbers 0 – 9, 26 capital letters, and some additional punctuation.
Next, in order to display these tiles, we will change the “main.c” from Part 1 to load these tiles and then display a pattern of them.
Running the build script will produce the following response when run in an emulator.
While not particularly useful as a first demonstration, it shows two things:
- Unless changed, the background remains whatever was last written to it
- The Game Boy hardware makes assumptions. In this case, when reset, it makes its background tile memory to be all zeros. Without other instructions, it took the first tile, a ‘0’, and repeated it across the entire screen.
To print “Hello world” we need one additional thing: an array of tile locations.
In programming Game Boy games, it is often much more convenient to produce a ‘map’ of where you want tiles to go and then use this map instead of arranging tiles one at a time. In fact, GBDK’s API is built with this in mind. Using an additional function set_bkg_tiles, we can write out a section of tiles at a time. However, before we can call it, we need that map.
Like with “alpha.c”, copy or download “helloWorld.c” and add it to the folder containing the other files.
To use the map, we need to update “main.c”.
After building the new files, we get a different response:
Note: Within the helloWorld array, I used “0x30” as a blank. This is tile position 48. Yet, remember, only 47 tiles were loaded. The 48th tile position, like with the other hardware assumption of showing all zeros, is because the background is reset, too. Without loading a new tile in that position, it will remain blank.
Finally, before getting to input and moving sprites around on the screen, we will do one more thing: create an additional blank map. As explained in the above note, the 48th position in the background table is blank. So, in order to create a blank screen, we can use this as we did with the helloWorld array. We simply create a new map of 20 tiles by 18 tiles (or 160 pixels by 144 pixels).
And, finally, adjusting our “main.c” to use this additional map, and to sneak in some user input, here’s the updated version.
Now, when we press ‘B’, we write a new map of ‘blank’ tiles over the old output, effectively creating a blank screen.
Note: It is far more efficient to set the first tile of the background table to be ‘blank.’ That way, by default, the screen will show either all dark or white, depending on your needs. However, for the sake of the above examples, it is worth showing how using the assumptions of the hardware against itself can save time and later effort.
Sprites work much like background tiles. They are loaded and used with functionality that works the same way internally. However, like with background tiles, we need some sprite tiles to work with before we can begin to move them around in any manner.
With the introduction of an additional file to include, we need to change “main.c” once again. We also need to introduce some new functions in order to use the sprites.
The function set_sprite_data works like set_bkg_data to load some array of values into sprite memory. set_sprite_tile assigns the first movable sprite to be some tile from the sprite tile set. In the above case, it uses the first tile in the sprite tile set.
Note: There is a hard limit to the number of moving sprites at a time set at 20 (0 – 19) with only 10 sprites moving per line. GBDK stores addresses for these moving sprites, which can be accessed through the set_sprite_tile function. To move larger sets of tiles at a time, especially in the case of larger visual sprites, multiple calls to set_sprite_tile are needed.
While our current code loads and prepares sprites for movement, it does not do anything quite yet. To move a sprite, we need the move_sprite function.
Now, after adding in some additional checks for user input and using the move_sprite function, we can move a sprite around on the screen using the directional input.
Note: The below GIF demonstrates one of my favorite aspects of the Game Boy: screen wrap. Without some type of checks in place, a sprite can move all the way ‘right’ and end up back on the edge of the left side.
With loading background and sprite tiles and then moving them around, we can make most of the games which came out for the original Game Boy. It may not seem that way, of course, but most of the ‘work’ in programming Game Boy games comes not in the coding itself, per se, but in memory and screen management. As with the hardware assumptions mentioned in this post, taking advantage of what the Game Boy assumes and making smart decisions about how to load things can speed up a project considerably.
In Part 3, I will cover two useful tools for helping with Game Boy programming projects: Game Boy Tile Designer (GBTD) and Game Boy Map Builder (GBMB).