z80 » Graphics
The basic concept of tile maps are to take several generic texture sprites, all of equal size, and print them on the screen in order with respect to a set of data cataloging the arrangement.
Think of a person that puts down bathroom tiles. He (or she) has a set of multicolored tiles and some sort of drawing to show him (or her) the pattern that they need to be laid out. That person will pick and place the tiles on the floor according to the drawing they have. The same goes for calculator tile map routines.
Games like Joltima, DStar, and Journey use tile map
routines to display the different objects on the screen. Each level in the game
has it's own distinct data representing a pattern on the screen. The game also
contains a table of sprites (all of
equal length) that coorespond to a value that can be in the level data. Say for
example the value
4 when found in the level data could refer to an 8 by 8 sprite
of heart while a
5 refers to a diamond. The tile map routine will:
- Take a byte in the level data
- Match that byte to its cooresponding generic sprite from a table
- Render that sprite on the screen
- Set up the coordinates for the next tile
- Move on to the next byte in the level data
Sprite Image DataTile map image generators use a set of sprites to make up the final image on the screen. These sprites need to all be the same size. The routines usually store the sprites all in order. If the routine wants the 3rd sprite and each sprite is 8 bytes long, then it will multiply 3 and 8 and add that to the starting address of the first sprite. Since most of the sprites are 8 bytes long, we can use our commands to multiply by 2 three times (2*2*2=8). Follows is how to store the data for two sprites. The first is a picture of a square; the second is a picture of a circle.
sprites: ;start of sprite data sprite_square: ;first sprite .db %00000000 .db %01111110 .db %01000010 .db %01000010 .db %01000010 .db %01000010 .db %01111110 .db %00000000 sprite_circle: ;second sprite .db %00011000 .db %00100100 .db %01000010 .db %01000010 .db %01000010 .db %00100100 .db %00011000 .db %00000000
Level DataLevel data for the sprite value can be stored in various forms: each byte, each nibble, every 2 bits, every bit. This all depends upon how many different sprite choices are available to the tilegenerator from the sprite data. If there are 256 different sprites in the sprite image data, then use a byte to store each tile's data; if there are 16 different ones, use a nibble; if there are 4, use every 2 bits; if there are only 2, use just one bit. The amount of possible tiles also weighs in to how bit the level data will be. For a 16 by 8 tile map, every byte being a tile would mean 128 bytes for each tile map's level data. If you had every nibble for a tile, that would cut that level's size in half to 64 bytes.
The tile generator routine is designed to be fast. The level data has to suit the routine. Say you have a routine, just so it can be fast, start drawing at the bottom right of the screen moving left and up. Your data might also need to start with the first byte being the bottom right, the second byte being the next byte to the left, etc. The routine should not have to accommodate the data.
Here's an example of level data (starting at the top left and reading from left to right, like a book). Following the data is a simple user chart telling you what each value encountered in the level data looks like as a sprite. These values are going to coorespond to letters. Following that will be a graphical representation of the TI86 screen after the tile map has been drawn.
level_data: .db 0,1,0,0,2,0,0,0,0,3,0,0,0,0,0,0 .db 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0 .db 0,0,0,0,1,0,0,3,0,2,0,2,0,0,0,0 .db 0,0,0,0,1,0,0,3,0,2,0,2,0,0,0,0 .db 0,0,0,0,1,0,0,3,0,2,0,2,0,0,0,0 .db 0,0,0,0,1,0,0,3,0,2,0,2,0,0,0,0 .db 0,0,0,0,1,0,0,3,0,2,0,2,0,0,0,0 .db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
a b c a a c b b a c b b a c b b a c b b a c b b aaaaaaaaaaaaaaaa
If you ever start to build a game like Joltima, DStar, or Journey, you probably want to consider writting a quick C/C++ application to make the level data. Probably have it use bitmaps of the actual sprites that will be used. If you can't do it in C/C++, then try using QBASIC or batch files somehow. If you end up making tons of levels, you're going to get a headache trying to figure out all the data and what it looks like in your head; a quick application can be a life saver!
The RoutineLet's construct a routine to generate the tile map for us. The basic routine,as stated at the top, will need to:
- Take a byte in the level data,
- match that byte to its cooresponding generic sprite from a table,
- render that sprite on the screen,
- set up the coordinates for the next tile,
- move on to the next byte in the level data.
In the Code Tree section, you learn that before you start designing a routine, it is best to outline the routine the same way you would an essay for school. Here's the indention outline for the routine we are going to make.
draw 8 rows draw 16 columns draw sprite get value from level data find sprite for that value draw 8 rows of sprite get byte from sprite image data draw byte onto video memory increase video memory to next row increase sprite data by a byte loop drawing of sprite increase column and repeat increase row and repeat
There are only a few routines out there that print tile maps on the screen. One of the most notable comes from Assembly Coders Zenith. ACZ is one of the top programming groups around. They're routine DrawMap is one of the fastest tile map generators. It uses GridPutSprite by Dan Eble in the routine. When I started making this tutorial, I decided to make my own tile map engine. When it was done, I ran it against DrawMap and saw it was over 10% faster running about 5 times more per a second.
I've pasted my TileGen Routine here. You can download this along with many other Tile Engine routines that I've come across in the Download Section. Each line in the routine is represented by a number which corresponds to an explanation at the end of the routine. Follow each line and try to picture in your head what it is doing. The easiest way to do this is to print out the TileGen Code and then refer to the explanations.
- ↓; tilegen by James Malcolm (firstname.lastname@example.org)
- ↓; draws 16x8 tile map
- ↓ ld ix,$fc00
- ↓ ld b,8
- ↓ push bc
- ↓ ld b,16
- ↓ push hl
- ↓ ld l,(hl)
- ↓ ld h,0
- ↓ add hl,hl
- ↓ add hl,hl
- ↓ add hl,hl
- ↓ ld de,tile0
- ↓ add hl,de
- ↓ ld de,$10
- ↓ ld c,b
- ↓ ld b,8
- ↓ push ix
- ↓ ld a,(hl)
- ↓ ld (ix),a
- ↓ add ix,de
- ↓ inc hl
- ↓ djnz draw_tile_loop
- ↓ pop ix
- ↓ ld b,c
- ↓ pop hl
- ↓ inc hl
- ↓ inc ix
- ↓ djnz loop_columns
- ↓ ld de,$10*7
- ↓ add ix,de
- ↓ pop bc
- ↓ djnz loop_row
- ↓ ret
|↑5||TileGen is called in context as follows:
ld hl,tile_data ;pointer to start of tile data call tilegen ;run tile generator
This routine can be modified. If you want to have the routine
draw the tile map to the graph screen so you can use a virtual
screen, just change line 6 to
Let's say you want to have a status
bar at the bottom of the screen. Just change line
ld b,7 and it will leave the bottom row empty.
You could also do the above change and move the start address
indicated by line 6 to start one row down
so you can have your status bar along the top. If you wanted
to have your status bar along the right, it gets a little
bit trickier. You need to edit lines 11 and
45 to account for not writing to the
right most column. Line 45 will have to
add 7 rows to go down another row and also over one column
since the previous time around it didn't make it to the right
You can have multiple tile sets by adding a few lines
at the beginning to accomodate the routine so that
de is loaded with the address of the desired
tile set to use. The routine would edit line
20 to write in that address instead of "tile0" with
the new address of the first tile in the new set.
ld (address of line 20+1),deMake sure to put a label above line 20 to so that you can reference it in the above code.
More from z80 » Graphics
Find a Pixel // Grayscale // Pixel Manipulation // The Screen // Sprites // SDR8 Routine // Tile Maps // TileGen Routine