C64 Tilemap/Level Editor - My First QB64 Project

in #programminglast year


This weekend I mostly spent my time learning a new-ish version of a programming language from the 1960s

I am a big fan of using project ideas as motivation for learning programming languages and technology stacks.

These project ideas don't need to be huge, in fact, it often serves me well to choose something "bite-sized" (pardon the pun).

One of the things that often happens is I need to build a tool to make my actual project, a tool for a tool kind of thing, and that is what happened with my C64 Dungeon Delve game that I mentioned previously.

While TRSE does have a built-in tile and level editor, it was developed for the Nintendo platform.

In the past, I used the excellent Charpad, but that is Windows software unless you use a VM/etc.

The famously brilliant CBMPrgStudio has a character editor, and can also run under a bottle/VM.

They are great options, but this was an excuse to build something ideal for me and learn another tool.

Enter QB64

Normally my go-to language for creating scripts and utilities is Python, but a little while back I heard about QB64, while I wrote about it briefly, I have been meaning to try it for something real ever since.

TL;DR It's a modern compiler and IDE for QBasic/QuickBASIC with lots of cool additions.

This means my compiled code will run on Mac, PC, and Linux. That's pretty awesome.

The Goal

For this program to be useful I need it to do certain specific things, but exactly how it does them is still open to interpretation.

First, it needs to work with the custom character set that I developed for the Commodore 64 version of my cross-platform game.

It needs to work with the binary file output of the character designer, and not be fixed, because as I create new maps and baddies I will be tweaking the graphics I need.

There can only be 255 of these user-defined characters so that is a fixed constraint that helps in the design.

The output of the program needs to be "meta tiles" to be used to construct maps.

As a balance between memory usage and flexibility, I decided the tiles would be made of 3x3 characters.

Different tools approach the creation of tiles in many ways. Some even prevent you from developing your own tiles, they just take in the characters and each 9 becomes a new tile. Most though treat the tile as a new picture in a carousel. That feels a little abstract to me, I need to see more tiles to get a better context.

I came up with the idea of being able to paint on a canvas, set up a grid, and use any of the characters as the draw or erase selection for left and right mouse buttons.

Eventually, the drawing will be constrained to character positions, but you can draw anywhere on the grid currently which is kind of fun.

The BASIC Code

QB64 has nice features for setting the screen resolution and drawing. I am using the line function for the boxes and lines in the grid, for example:

LINE (140, 10)-(308, 190), , B

It's also turned out to be really easy to load the character binary data from the data file too:

OPEN tiles_file$ FOR BINARY AS #1
FOR this_char = 0 TO 256

    FOR row = 0 TO 7
        GET #1, , charset(this_char, row)


This opens the file on disk as binary and assigns it to File 1, then I load up my character array of 8 bytes using GET #1.

Screen Shot 2022-01-24 at 10.46.45 AM.png

Mouse Control

Obviously, seeing as I want to 'draw' on the screen, I need to utilize the mouse.

QB64 does allow you to use the mouse scroll wheel but on my mac it wasn't recognized, that would have been fun for selecting the currently in use character, but left and right mouse clicks are all I really need.

Screen Shot 2022-01-24 at 5.14.28 PM.png

Drawing the Character

Once I had my binary data in a two-dimensional array, I can simply look up a character by its number from 0-255, and then draw each of the 8 rows of 8 pixels.

This isn't the fastest way to do it but it is sufficiently quick to be fine in action.

SUB print_char (which_char AS _UNSIGNED _BYTE, x AS _UNSIGNED _BYTE, y AS _UNSIGNED _BYTE)

    DIM this_byte AS _UNSIGNED _BYTE
    DIM this_bit AS _UNSIGNED _BIT

    r = 0
    b = 0

    FOR r = 0 TO 7

        this_byte = charset(which_char, r)

        FOR b = 0 TO 7 STEP 1

            this_bit = INT(_READBIT(this_byte, b))
            IF this_bit = 0 THEN
                PSET (x + (7 - b), y + r), 12
                PSET (x + (7 - b), y + r), 1
            END IF



Next Steps

Next up will be to constrain the drawing, then allow the user to save the tiles out. Saving will mean iterating through which character is used whereabouts in each 3x3 grid, so I might as well have another two-dimensional array to represent tile number and the 9 selected characters for that tile.

For creating a map it will be then a case of placing a tile on another grid and saving out which tile needs to go where in that level.


Hi there, this is a pretty interesting topic, especially since the Programming scene on Hive needs more posts like this one.

If I could make a suggestion, a little more depth on the post or maybe a more thorough explanation of the step by step (think of a kind of tutorial for those who like coding but might not be familiar with QB64) you are following might be very useful (and also get more attention from the dev audience).

Looking forward for the next part of the series :D

Thanks :)

I do have more comprehensive programming tutorials here, I wasn’t really expecting a lot of interest in this one due to the unsexy topic ;)