"Automating Game Tile Creation"
PC Techniques, Vol. 5, No. 1
Apr/May 1994, page 65
What appears here is the original manuscript, as submitted to Jeff Duntemann. Any changes in the published version are due to Jeff's expert editing. There is a code file that goes with this, see QFRIP.ZIP.
Game programming is not all glamour and code, a lot of it is quite tedious. One of the most tedious jobs is preprocessing artwork so that it will fit into a game. Sprites, after they are drawn, must be measured and cataloged and indexed and stored, so they can later be allocated and read and displayed. Likewise, background tiles must be arranged in a file and indexed so they can be rebuilt into the marvelous large scrolling worlds necessary for action/arcade games.
A game programmer will find his development time greatly reduced if as many of these tasks as possible are automated. Sprite editors trim and measure and store sprites. Level editors build levels out of blocks of tiles. But where do the tiles come from originally? Your artist creates a picture and sends it to you in a PCX file, and you must break it down into the minimal number of tiles required to create the background. Doing this by hand would require painstakingly cutting out 16x16 areas and visually comparing them to other areas on the screen, then storing and indexing them -- a nearly impossible task! Small changes to the artwork would require the work be started over again from the beginning. Even if you don't make any mistakes, a job like this will take you all day. There has to be a better way.
The answer, of course, is to write a tile ripper. A tile ripper will take raw artwork and reduce it to its elemental tiles. The basic concept is quite simple. The artwork is displayed on one video page. The ripper starts at the upper left corner, picking up a 16x16 bitmap, and storing it in RAM and on another video page. Then it picks up a second bitmap and compares it to the first copy in RAM. If it finds a match, it throws out the second tile, and indexes the level map with the first tile. If there is no match, the new tile is stored on the hidden page, and the tile library is increased by one. Subsequent tiles must be compared to the first tile, the second tile, and any other tiles which are found to be unique. Eventually, the tile page contains only unique tiles, and the picture is stored as an array of indices into the tile library.
As the tile ripper progresses, it "blacks out" the duplicate tiles, leaving only the unique tiles visible. There's no good reason for doing this, except it makes the rip more fun to watch.
After the ripper has reduced the picture to its elemental tiles, it writes out the level to a binary file, and also creates a pcx file containing the tiles. Finally, the ripper checks its work by reconstructing the original picture from the tiles, and displaying it on the visual page.
The tile ripper will read and process more than one input file. The total number of input files will vary according to the complexity and amount of duplication in the artwork. The Quickfire background has a lot of blue sky, and all the completely blue tiles are identical. I ripped approximately five pcx files of background art to create the Quickfire sky.
A tile ripper should not be confused with a level editor. The purpose of a level editor is to build game levels by moving around blocks of tiles, adding and deleting rows and columns, changing the tile attributes, etc. I will release some level editor source code in a future article. The tile ripper creates the raw levels that are used as input to the level editor. Once you have the ripper data, you can use the level editor to create create several levels out of one tile set. In the case of Quickfire, I created a vast expanse of blue sky, and placed the clouds on it in such a way as to give the impression of a large, non-repeating background.
Our tile ripper will generate no more than 240 unique tiles. This number was chosen for several reasons. It is the number of 16x16 tiles that will fit on one 320x200 screen. Having no more than one screen of tiles makes them easy to manage. You can view the tiles with a paint program or level editor, and you can store them all on one page in video memory. Perhaps most important, if you have fewer than 256 tiles you can define your level as an unsigned char array. If you have more than 256 tiles, you must use integers in your level array, which will double its size. Since level arrays are allowed to become quite large, this is a non-trivial consideration. It could be the difference between storing your level array in near memory or far memory, for example, which is the difference between using the medium and large memory model. Changing memory models is more than a size consideration, it also causes a potential degradation in speed. A larger level array will also mean you have less room in RAM for sprites and music, and you may have to do more disk accesses.
Of course, like all game design decisions, one must always consider the trade-offs. If you have compelling reasons to allow more than 240 tiles in your tile library, then by all means modify the tile ripper to accomodate them. You can store the extra tiles on page 2 or page 3, which are not being used by the tile ripper. Fitting them into your game is a another matter, but it can certainly be done.
A tile ripper is a useful utility that will save a game developer time and aggravation. The dedicated game developer will have a personalized collection of such utilities and will be adept at using them. Any time you find yourself doing a repetitious task, try to find a way to get the computer to do it for you. A few hours spent developing a utility can shave weeks off the game development cycle.
+------------------------------------+
| |
| +-----------------------------+ |
| | | |
| | | |
| | \/ \/
| | +------------------+ +-----------------+ +--------------+
| | |oooo| | |oooo|xxxx| | |oooo|
|
| +--| 0 | | | 0 | 1 | |
+->| 0 | |
| |----+ | |----+----+ | | |----+
|
| |oooo| | | | | |
| |oooo| |
| | 1 | | | | +------------+->
| 0 | |
| |----+ | | +----------------+ |----+ |
| |xxxx| | |
| | |xxxx| |
+----| 2 | original | | | +->| 1
| |
|----+ picture | | | |
|----+ |
|xxxx| | | tiles | | |xxxx|
|
| 3 | |
| | +->| 1 | |
|----+ | | |
|----+ |
| | |
| | reconstructed|
| | |
| | picture |
+------------------+ +-----------------+ +--------------+
visual page hidden page visual
page
before rip
after rip
In this example, tiles 0 and 1 are duplicates, so 1 is discarded. Tiles 2 and 3 are also duplicates, so 3 is also discarded. Tiles 0 and 2 are the only unique tiles, and they are renumbered to 0 and 1 and stored on the tile page. The picture is reconstructed by displaying multiple copies of the two unique tiles.