Introduction
Recently I have been writing some routines for a PC game that is 2D and needs some backgrounds on which to play. I had only done some space games on PC before, which cunningly only need a starfield backdrop. I used a photo I took of the Milky Way on a clear night from my garden.
![]() |
PC Monster Molecules |
I knew that the time would come when I would need some nice scrolling backgrounds. I even wrote a routine to draw a background from arbitrary sized squares and cunningly used my starfield photo as the source squares, or tiles. I incorporated code to flip tiles in X or Y, and give them individual palettes, everything I could think of that the retro arcade machines could do. Everything I will need to write retro games.
Computer Hardware
Some 8-bit computers had character tile modes. That is where the screen is built from a small number of 8-bit character codes, and the video chip looks up the data to draw the characters. This may be just an alphabet, some punctuation and some rudimentary generic graphics. Forward-thinking computer designers allowed the software to redefine the character set with all user-defined graphics, sometimes in multi-colour mode so that artistic things could be done.
C64 games were often done with user-defined graphic "tiles", You get up to 256 of them and can use split-screen techniques to change tile sets for separate score panels. The actual screen can be up to 40 tiles wide by 25 high, which means the screen size can use 1,000 tile characters, which is slightly more than the CPU could update in a 50th of a second, which is why most scrolling games have a smaller play area than the full screen.
Game Maps
Any scrolling game will have to have a larger map of tiles that are used to create the play area. Games that only scroll in one direction can potentially unpack the map as they go along. These type of games, such as Scramble or R-Type, tend to move slowly along to reveal more new scenery. I tended to prefer a free world where you can go where you want, scrolling freely in 2 or 4 directions. That tends to mean that the whole play area map is unpacked and laid out in memory, ready to update the screen. Now these maps tend to be quite large. Since one screen is 1,000 bytes big, then a play area of tiles might easily be 16 times larger, that only gives you 4 screens wide and 4 screens high. 16,000 bytes is a quarter of your C64 used up. A game will also have a number of levels, shall we say 16? Suddenly that is 4 C64s`-worth of space used up if we didn`t somehow compress the data. In our single load games we need to be able to store all the compressed data for those 16 levels as well as having a 16K buffer for for one map, and all the game code, and all the graphics for the tiles, and the sprites.
We need to be able to efficiently compress all the level data, and additionally we need to be able to design and specify all of the game maps in the first place. Back in the day, I designed all my own graphics for fonts, sprites and backgrounds. As a programmer, I might well have my own ways of designing the level data that a more modern graphics artist would not tolerate.
A game designer has to decide how to create the game maps and how to compress them. We used to meet other programmers from time to time and some of them would proudly tell me about their tile mapping utility they had written to create their game maps. Sometimes they might even leave them in the final game. I would always ask them 2 questions. Firstly, how long did it take them to write the mapper, and secondly, will they use the same mapper for another game? They might well answer "6 months" to the first question, noting that their utility was likely customised for their own game and not necessarily generic enough to sell to anyone else, which answers the second question: "No, it might not be used again." Knowing that I could write a whole game, draw all the graphics and create all the map data in 5 months or less, I was reticent to even suggest to the boss that I write a map editor, with no experience.
Examining Past Efforts
As all C64 programmers know, the VIC-II graphics chip with its tile mapping capabilities can perform a number of tricks that can`t be done as easily on a bitmap screen. One can alter a single tile image and every instance of that tile on the screen gets updated for free on the next 50th of a second display frame. With that, we can animate characters, do parallax layers, make things dissolve and reappear, or move objects across the background. Tiles aren`t just for decorating the bathroom, these are alive. Programmers therefore need to reserve some tiles for graphics tricks, and it can also be wise to have control over what tile goes where in the set of tiles so that collision detections and orderly transformations can take place, such as blowing up ships on the runway. Duplicate graphics can also be used as secret markers to control game elements, such as Gribblets reserving their new landing spot as they take off to avoid collisions, and knowing when they are in the home cave. Rules are created that don`t interest graphics artists, and no-one wants to have to rearrange all the tiles after the maps are drawn. It wouldn`t surprise me if all mappers have a function to swap tiles around in the tiile sets when the programmer has another "great idea".
Gribbly`s Day Out
My first game with a tiled background, had me falling into line with other C64 game designers and arcade games of the time. I wanted an organic look to the scenery, which meant trying to hide the square edges of the tiles. I was using sprite to background hardware collision detection for pixel-perfect collisions, which is almost free in terms of CPU time but is also painfully perfect. I had to then use the colours carefully since only two colours of the background caused collisions with sprites.
In order to build the background maps, which had to fit into 256 characters wide by 64 high, I did some preparatory work. The edges of the buffer were filled in with side walls and a top ceiling, and then some base characters of the floor. I over-simplified the scrolling and co-ordinate system to lock Gribbly in the middle of the screen rather than locking the scroll near the edges and let him approach them. That wasted some space, sorry! I then applied a block of tiles all over the map that contained the triangular energy barriers and the control buttons. This initialises all of the buffer to the blank sky character. I believe that left a couple of uninitialised character lines at the bottom. Most of the maps would then need a nice piece of land to bounce around on, and maybe some water.
To set up the bottom of the map, I then designed some blocks of tiles of arbitrary sizes and assigned them unique numbers and input assembler declarations of bytes for the width of the block and then for each column a height byte followed by the tile characters in the column. The blocks might only be 1 tile high and a a few tiles wide for a stretch of grass, or be a bit of lake with a large rock formation sticking up. I would have an address lookup table to get from the block number needed to the actual block data. There would then be a simple list of block numbers to fill the width of the map from left to right.
![]() |
C64 Gribbly`s Second Day Out |
At this design point, I realised that there would be a lot of blocks for level land to move about on, some blocks to bolt to the sides of the map, and I would need some craggy rock formations that provided the underside of higher platforms. Following the setup of the bottom pieces, the list became busier as I then had to provide positions for all of the remaining scenery blocks. I tried to make the major rock lumps quite big, and fit together at the sides so as to reduce the number of blocks needed. The list was then made up of groups of an X position, a Y position and a block number. All of those fit in a byte each, keeping the data compact and bijou.
Next I realised that some of the scenery would cut across energy barriers in the sky, and also I would like to switch off or remove some energy barriers to make flying routes, and maybe also remove some buttons so that I could create locked areas, barriers both on and off. There were tile blocks to switch barriers off in the 3 faces of the triangles in one go. I hope I was smart enough to remove barriers and then plot in the rocks afterwards. It was easy to swap the sequence of the blocks by cut-and-pasting the list of blocks. Later blocks tended to get smaller as I might want to patch in small corrections.
Levels tended to take about a day each to enter, test and adjust. The tougher screens took longer because I had to practise getting through some of the tight gaps. If they were too tough then I might have to move a lot of scenery so I did build them up in stages and test them a bit at a time. I could correct small mistakes and adjust the difficulty at the same time.
Paradroid
I reduced the background tile maps to 160 displayed characters wide by 64 high. This game though was set on a space ship and viewed from top down. Straight lines were the order of the day. I figured I could build the decks of the ship from fixed size blocks and therefore they would all fit together. Tile blocks of 4 by 4 characters were created, I ended up with 32 of them. I already had it in mind to have a miniature deck display where the 4x4 tiles would be represented by just one character each.
![]() |
C64 Heavy Metal Paradroid |
I was still entering the map data into assembler source files and chose to pack the map data down some more by using run-length encoding. I may have used the remaining 3 bits of the tile codes as a count of 1 to 8, which suited horizontal walls and corridors nicely. So the maps were just a list of bytes containing a tile code and a number of them. I didn`t bother with a second list of alterations as it likely wouldn`t have saved much. It was much easier to set up the maps. I had them all drawn out on graph paper as all the lift shafts needed to fit together properly to feel like a real place. It was then a case of counting the blocks and typing them in. Naturally there were frequent errors and miscounts as the decks would sheer somewhere and I would try to patch the data and thus be able to enter multiple corrections in the source code together in one go.
I can`t remember exactly, but I would have had the whole ship entered in about a week and didn`t alter it much, if anything, as the ship design provided encounter areas of various sizes and was mostly non-negotiable in shape. I may have added extra energisers later, but that was about it.
Uridium
Uridium came along next and with shadows being cast from tall obstacles, the 4x4 block idea was going to be limiting, so I resurrected the Gribbly`s system. I created tile blocks of the height of the screen and filled the smaller map buffer, now about 19 characters high and 256 wide still, from left to right. This created the basic dreadnaught platforms with nothing on them. Then I was able to use the secondary Gribbly`s system of adding arbitrary-shaped tile blocks onto the dreadnaught platforms. I could do two levels in a day as this was a straightforward process and the difficulty of playing the levels ultimately decided their sequence in the game. While i may have had an idea to make a difficult level or an easy one, I could later compare them and swap the data lists about.
![]() |
C64 Uridium |
The new level being worked on was always tested as level one. What`s the point of playing all the way through to level 11 before you get to test the new data? I would let my test team lads have a play and they would let me know if something was too difficult, or too easy!
Alleykat
I always like to change things from game to game because, to me, being accused of re-hashing the old stuff would be a horror. Also, one does tend to expend a lot of ideas in a game so it`s best to reset with something new. There might be some ideas that couldn`t go into the previous game. I went for a vertical scrolling game instead here, so I reconfigured the map buffer to be 40 characters wide and 256 high, which would have saved me some valuable bytes for extra graphics sets. There were 8 different sets of tiles in there.
I suspect that I was getting tired of entering all of the map data because I decided to write the routines to generate the race tracks from a set of parameters and some predefined shapes. I had background blocks that were up to 5 character tiles wide and 3 high, and all the 8 graphics sets were interchangeable. The tracks were then defined by how far apart the rows were and what the density of big pieces was. That also decided how many gaps there might be to drive through, how many energy blocks there might be to pick up, and the collapsibility of the pieces.
The map algorithm cleared the buffer to the blank ground piece, then randomly set up the lines of scenery such that as it filled the lines from left to right, it would exactly fill the row with pieces, nothing cut off mid-way. Then it would sprinkle a couple of different ground tiles in the gaps between, along with energy pickups. All I then had to do was select some eye-catching colour schemes.
![]() |
C64 Alleykat |
Maps then were still made from some predefined blocks of tiles, but the random number generator decided on the final layouts.
Morpheus next, which had no tile maps.
Intensity
This game had 70 different single-screen levels, all made from tile map lists. I chose Uridium for the graphics style and the tile block methods, but added a new type of tile block, the 3-by-3 expandable rectangle. I realised that I could use 9 tiles, with the option of a ring mode with a missing middle. Now I could draw any size rectangle, right down to a 2-by-2, with a string of 5 bytes for the position and size of a rectangle and a last number for the block layout, of which there might not have been many. Likely at this time then I would also have used a simple repeat of a single block either horizontally, vertically or even both.
![]() |
C64 Intensity |
Platforms could be laid out roughly with the rectangles and then decorated with details using the usual X, Y position and block number. I did then generate a height map from the tile codes and then from the height map I set up a desired altitude map around the high points. That process involved effectively plotting a tile block of heights onto the altitude map but assigning the highest value of of the tile and what s already on the map.
Rainbow Islands
In 1989, when we worked on Rainbow Islands, we didn`t know how the level maps were laid out in the arcade machine ROM. The designers of Rainbows Islands at Taito might have had a map editor to create the backgrounds, this seems likely, and they might have been stored raw on big ROM chips on the arcade board. More likely they would at least have been compressed with a generic compressor and stored packed and unseeable. So even if we had the kit to go looking at the ROMs, we might not have found anything useful as compressed data requires the decompressor software.
We chose to video the whole game. John Cumming: our programmer and graphics artist, set about writing a map editor using STOS, a BASIC platform with graphics additions, for the Atari ST. This took him a couple of weeks. He then had to sit there with a VCR and our video of David O`Connor, our best arcade player by far, playing through what we thought was the whole game of 7 islands. John would pause the tape and recreate the background. We did get some early level graphics sheets from Taito with the tile sets on. We still had to map them to less colours as the arcade machine could display more colours than the ST. John spent another couple of weeks generating 28 level maps of ever-increasing size.
![]() |
Enhanced Rainbow Islands for PC/PSX/Sega Saturn |
We then hit the problem of how we were going to compress 28 levels of data that was up to 256 tiles high by 40 wide, an estimated 450K of data that needed to fit 4 levels at a time in the target machines. These were 8x8 pixel tiles, which now seems a bit unusual given that the arcade machine had a 16-bit CPU in it, but quite possibly the graphics chip was still configured for 8-bits. We followed suit on our 16-bit platforms, knowing that we also had to produce 3 8-bit versions too. It was at this point that David observed that the graphics for the backgrounds were often built from 4 tiles in squares. These would repeat all over the place. We therefore set about writing our mega-compressor that would consider the 4 level maps of each island, looking firstly for adjacent pairs of the same characters, and substituting in a single macro code for the pair. It could then look vertically for pairs of tile codes or indeed new macro codes that occur more than 3 times. It would then substitute another macro, this time a vertical one. Then we might do another horizontal pass looking for patterns 4 characters apart. Then again with vertical. We could do up to 8 passes.
The end result would be a table of macro pairs and spartan maps with macro codes and usually big gaps between them. We could then pack the maps down and record the lengths of the gaps as one code instead of all the blanks. On the one hand this produced excellent packing results, on the other hand it did take the Atari ST an hour of huffing and puffing to produce the data. We then had to write the decompressor code as well that sits in the game code to this day. Unpacking is pretty fast as it only has to go through a level once to put the gaps back in (starting at the end of the data), and then 8 times to unpack the macros per pass in either horizontal or vertical mode. Packing had to pass through 4 levels of island data for every macro it created, looking for every instance.
Paradroid 90
I first tinkered about with 32x32 pixel tiles in 4 colours and had a 4-directional scrolling demo on the ST. The sprites were 16-colour but we thought that the backgrounds didn`t look 16-bit enough. There were some nice colourful games coming out and we`d have been shunned with only a 4-colour background. We were wedged firmly between a rock and a hard place, because avoiding horizonal scrolling got us grief from the Amiga crowd.
![]() |
Amiga Paradroid 90 |
We decided, since we were supporting the Atari ST and Commodore Amiga, that we would continue with 8x8 graphics in the same manner as Rainbow Islands. Paradroid had used 4x4 tile blocks that would suit our mega-compressor still, but now we had a mapper program and while I designed the ship layouts, we could let the graphics artists do the decorating of the levels without having to stick to the strict 4x4 tile blocks. We expanded the mapper program to support up to 16 maps for compression so that we loaded in one ship at a time. The downside of all this efficiency was that the mega compressor took 90 minutes to pack and save the map data for a 16-deck ship.
No new tile mapping techniques this time, just a lot more data.
Fire & Ice and Uridium 2
We finally went 16-bit proper and optimised our game systems for 16x16 pixel tiles. I believe we had a new tile mapper program, likely on the PC, written in DOS, being pre-Windows, and certainly in 16-bit.
The sort of feature that you need in a mapper is to pick up a block of tiles and then plant it in multiple places, which is not so dissimilar from using instructions to plant blocks of tiles. When one uses a mapper program, inevitably a generic compressor gets used, which means you need to know how to decompress the data and have that in your game code. The latter is not necessarily available unless you write both halves yourself, or you know someone who has. We knew Factor 5, thanks lads.
a![]() |
Amiga AGA and CD32 Fire & Ice |
The maps for these games were created mostly by the graphics artists and were sufficiently detailed and varied that a generic compressor would have as good a chance as any to pack the data down well. Fire and Ice level maps were typically 8 to 10K before compression with up to 5 per land. Compressed maps were combined with land-specific graphics, sound samples, music and meanie control data and might run to a total of around 70K.
![]() |
Amiga Uridium 2 |
The Present
Fast-forward 30 years and I find myself needing a tiled playfield for a game on PC, I`ve done a space game and now I need a top-down background that scrolls. I have the software to plot a background with tiles of any size I choose, and with typical screens being 1920x1080 pixels, I need a lot of background data. I tried my setup on a 4K monitor and my 64x48 tile map of 64x64 pixel tiles barely scrolled at all before running out of data.
Wrestling with the enormity of the task of writing a tile mapper program, I still resist and spent a few hours writing all of the code to do what my entire 8-bit catalogue did for tile mapping. Ultimately, it came down to a loop within a loop within a loop within a loop. I can define 4x4 tile blocks and specify blocks in the Paradroid style or arbitrary blocks of tiles to be plotted consecutively or by specified positions, with or without repetition.
![]() |
PC Project 3 (Name undecided) |
I then realised that I can go further with more functionality such as a flood fill, which is great for filling in the gaps in debug mode before the map is complete. Further than that, functionality is there to maybe swap in alternate tiles in areas for graphical variety rather than obliging a graphics artist, or me, to spray alternate graphics randomly. I can also flip tiles in X and/or Y and apply a 90 degree rotate, so a randomiser of solid textures might be able to arbitrarily flip certain blocks for more variety.
Here`s a teeny bit of C code that generates my test map:
long Wave1BlockList[] = {
PBlock1x1_XY(1,1,UB_Small_Tower) // Single blocks are directly specified
PBlock1x1_RXY(4,5, 34) // Including repeated ones
PBlock1x1(UB_Small_Tower) // Place another tile after the last block
PBlock4x4_XY(0, 6, Crossroads_4x4) // Paradroid-style crossroads
PBlockNxN_RXY(12,1, RoadHorizontal_NxN) // Add a road to the right
PBlock4x4(Crossroads_4x4) // Add another crossroads
PBlockNxN_RXY(12,1, RoadHorizontal_NxN) // Then some more road
PBlockNxNVertical_XY_RXY(0,10, 1,12, RoadVertical_NxN) // Add a vertical road frpm the 1st cross
PBlockNxN_XY(14,7, ZebraVertical_1x2) // Put in a zebra crossing
PBlockNxN_XY(1,14, ZebraHorizontal_2x1) // and another zebra
PBlockNxN_XY_RXY(0,17, 1,2, LevelCrossingHorizontal_4x1) // Add a train track across the road
PBlockNxN_XY(23,6, LevelCrossingVertical_1x4) // Add another train track over a road
PBlock1x1_XY_RXY(12,0, 20, 6, UB_Earth) // Create a muddy patch of forest
PBlock4x4_XY(4,10, HedgedBuildingTL_4x4) // Create a small hedged plot
PBlockNxN_XY_RXY(0,36, 64,1, EastWestRiver_1x5) // Make a long river
PBlockNxN_XY_RXY(0,41, 64,1, SouthCoast_1x7) // Make a beach and some sea
PBlockNxN_XY(27,23, HedgedBigBuilding_NxN) // Put a big building near the river
PBlockFill_XY(49,5,35) // Fill all the spare space with railway track
PBlockEnd
Torture, or what?
July Addendum
I added a postprocessor so that after the tiles I listed have been plotted in the map, I can identify block edge-pieces that have an auto-fill bit set. That way, I can, for example, have a lake or sea with an auto-fill edge that then bleeds out to any adjoining empty space. That means I don`t need to explicitly fill areas. Makes the job shorter and therefore quicker.
As I alluded to, above, I also used another couple of ordinarily mutually-exclusive bit-settings to indicate that a block could be randomly flipped in X, Y, both and/or rotated to give variety. Tiles of one texture can generally be flipped any which way and/or rotated. I have tiles that split into horizontal bands of two or three textures so they can be randomly flipped in X, though they can be flipped in Y and/or rotated under strict control too. These can be specified explicitly in the tile blocks, or the flood-fill requests or the final auto-fill pass, since the random flipping etc can be identified by the low-level tile plot routine. That`s where structuring the code is vital to use single functions and not repeat code.
I`m now considering how to get some initial tile data into the maps automagically as I increased my current map to 128x96 tiles, which is plenty big enough for a slower-moving game, even on a 4K screen. If I use strings of characters to represent 4x4 Paradroid--style tile blocks, I would only need 32x24 characters. With that, I could put in the roads, railways, and basic areas on the map and then add my specific arbitrary-sized tile blocks to put in the details. I wonder whether I can get an algorithm to do that?
I am going to use the tile map to generate objects. Each tile number can generate one type of object. I have some algorithms such as a random chance of making a tree, I can put street lamps at specific intervals and hedges round the houses or fields. That may require similar-looking blocks, some that generate one thing, some another and some not at all. It all adds to the variety of a natural environment. I may be able to introduce a height-map and tune the block tints a little to get some shape to the landscape, though I am also using the overall tint for daylight effects. We`ll see.
Conclusion
Whether you have a map editor or generate maps with commands is all about art versus technology. Both work and if you`re an artist who can program then you`ll likely write a map editor, whereas if you`re a programmer with some graphics savvy you`ll probably want to get on with creating some maps with data. Your data starts off compressed and you already have the unpacker. If you do want to write a map editor, then be prepared to have a lot of functionality, since we programmers do like to change our minds often.
Add a comment