Dynamically creating tiles - GBDK
How does a Tile work
THIS TUTORIAL USES GBDK & GBTD
To generate a tyle you can call a DMA method to write from cpu and ram to the VRAM. in GBDK it's your standard "set_?_data" types that handle these actions. I personally used the "set_bkg_data" as these are not limited like sprites are, allowing you to create a full scene of animated objects and graphics to your hearts content. And possibly overlay these with sprites as "UI" elements or additional game elements.
To find out how to write a tile you can create tests using the GameBoy Tile Designer, and creating a drawing like this to find out how these are tiles are set up. (Export as .C for GameBoy/GBDK)

//the code will look something like this
unsigned char TileLabel[] = {
0x80,0x80,0x88,0x88,0xCC,0xCC,0xF0,0xF0,
0xFF,0xFF,0x80,0x80,0x00,0x80,0x80,0x00
};
//let simplify this by making pairs of 2 and add a row number to each row.
unsigned char TileLabel[] = {
0x80,0x80, //y1
0x88,0x88, //y2
0xCC,0xCC, //y3
0xF0,0xF0, //y4
0xFF,0xFF, //y5
0x80,0x80, //y6
0x00,0x80, //y7
0x80,0x00 //y8
};
Notice how a tile is 8 rows long and 2 bytes wide (2bytes = 16 bits), which doesn't match 8 pixels wide. Take a look at row 7, 8 and 1 in this order.
// y7 = bytes[] { 0x00, 0x80 } = light gray
// y8 = bytes[] { 0x80, 0x00 } = dark gray
// y1 = bytes[] { 0x80, 0x80 } = black
So to create a black pixel both bytes need to be set the same. But how do we know which pixel is what. The trick is using the bits from a byte as 1 byte = 8 bits. The pixels are directly correlated with these.
see as y1 = uses { 0x80, 0x80 } if you only focus on 0x80 and convert it to binary it is equal to "1000 0000"
y2 uses { 0x88, 0x88 } which in binary is equal to { "1000 1000", "1000 1000" }
y5 is a full black row of pixels { 0xFF, 0xFF } is equal to { "1111 1111", "1111 1111" }
now if I want a full LIGHT gray row in binary you would write { "0000 0000", "1111 1111"} in bytes this would be { 0x00, 0xFF }
similarly a full DARK gray row in binary you would write { "1111 1111", "0000 0000"} in bytes this would be { 0xFF, 0x00 }
Generating a Tile in Code
THIS TUTORIAL USES GBDK, recommended to test the game with BGB
Now we know how to formulate and manually write/design a single tile, let's create one fast and efficiently in code. To start off, copy a (bare-minimum) example project, open main.c and create the following global varriables:
- array named vramData with one tile's worth of bytes (16 bytes)
- array named vramTiles with 1 tile description;
- single x var.
- single tileBase var.
- single tileOffset var.
unsigned char vramData[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
unsigned char vramTiles[] = {
0xFF //last vram tile
};
UINT8 x = 0x00;
UINT8 baseTile = 0x40; //will be used for setting the standard vram position
UINT8 offsetTile = 0x00; //will be used for multiple tiles
This is the one tile to rule them all, you don't need any additional array's for multiple tiles. To go about rendering create a method named computeTile here we will for simplification write all bytes manually (you can replace this with for loops or your own implementation later). We will however loop over the X value; This will generate a vertical line that fills up the space.
void computeTile()
{
vramData[0x00 + 0x00] |= 0x01 << 7 - x;
vramData[0x00 + 0x01] |= 0x01 << 7 - x;
vramData[0x0 + 0x00] |= 0x01 << 7 - x; //y1
vramData[0x0 + 0x01] |= 0x01 << 7 - x;
vramData[0x2 + 0x00] |= 0x01 << 7 - x; //y2
vramData[0x2 + 0x01] |= 0x01 << 7 - x;
vramData[0x4 + 0x00] |= 0x01 << 7 - x; //y3
vramData[0x4 + 0x01] |= 0x01 << 7 - x;
vramData[0x6 + 0x00] |= 0x01 << 7 - x; //y4
vramData[0x6 + 0x01] |= 0x01 << 7 - x;
vramData[0x8 + 0x00] |= 0x01 << 7 - x; //y5
vramData[0x8 + 0x01] |= 0x01 << 7 - x;
vramData[0xA + 0x00] |= 0x01 << 7 - x; //y6
vramData[0xA + 0x01] |= 0x01 << 7 - x;
vramData[0xC + 0x00] |= 0x01 << 7 - x; //y7
vramData[0xC + 0x01] |= 0x01 << 7 - x;
vramData[0xE + 0x00] |= 0x01 << 7 - x; //y8
vramData[0xE + 0x01] |= 0x01 << 7 - x;
set_bkg_data(tileBase + offsetTile, 1, vramData);
}
void main(void){
//...all in main
// Game main loop processing goes here
computeTile();
x++;
if(x >= 8){
vramTiles[offsetTile] = baseTile; //set the current tileset
set_bkg_tiles(37, 35, 1, 1, vramTiles); //posX, posY, tileWidth 1, tileHeight 1, tileSet
//to make it flicker
UINT8 clearVRAMI;
for(clearVRAMI = 0x00; clearVRAMI < 0xFF; clearVRAMI++){
vramData[clearVRAMI] = 0x00;
}
x = 0x00;
}
offsetTile = 0x00;
}Get [GameBoy] Technical Demo - Vertical Scanlines/RayCasting
[GameBoy] Technical Demo - Vertical Scanlines/RayCasting
Prototyping a FPS: writing from CPU to VRAM (DMA) and RayTracing like "X" or "Faceball 2000"
More posts
- RayTracing RequirementsSep 28, 2021
Leave a comment
Log in with itch.io to leave a comment.