Pixelate:Issue 11/Tile Engine Goodness
From Allegro Wiki
| Tile Engine Goodness | |
| Original author: | Nathaniel Sabanski |
|---|---|
| Website: | |
| zip: | P11 Test engine.zip |
If you dont already know, this tutorial is for people who want to learn how to program a tile engine. The code is in C/C++ and uses the Allegro library but the code could easily be converted to any other programming languages and other librarys.
This tutorial is divided up into the following sections:
- [#Words Words to know]
- [#Whatisit What is a tile engine?]
- [#whatdoineed What do I need to do this?]
- [#whatcode What does the code look like?]
- [#conc Conclusion]
- [#Credits Credits]
Words to know
Before we begin there are some vocabulary words you will learn about. I have included the following glossary for reference:
- Tile Engine: [#Whatisit What is a tile engine?] - Tile Map: Used as 'building instructions' on what tiles should be laid. (Usually an array). OR tile map can also mean a compiled level or image made out of tiles. - Blit or Blitting: The process of copying your graphic to the screen. - Buffer: A Buffer is used to hold a canvas of graphics to blit to the screen. - Total redraw: Each frame draw the whole map and add a x and y to each tile's location then change the x and y to scroll the map.
[tileenginegoodness.html Return To Top]
What is a tile engine?
A tile engine is a way to make fast, efficient, easy graphics for computer applications (specifically games). It achives its greatness by using a series of premade 'tiles' and 'compiling' or 'laying' tiles onto the screen (or onto a buffer being blitted to the screen in our case) using a 'tile map' (usually an array) to make an eventual on-screen graphic. As you can see this way could be very efficent.
For example, say you are making a 'Super Mario Brothers' clone. This game is a platformer/sidescroller, with very large and long levels. Now, who would have the insanity to just go through and draw, pixel by pixel, every single level in the game? Certainly not Nintendo. Obviously this game uses a tile engine. Not saying that you could NOT go and draw every level by hand, and just blit the whole graphic to screen, but that would take up massive space and time. If you look around you will find TONS of games that use tile engines. You will also learn that tile engines can be used for many different types of games and not just platformers. Some well known examples would be: Final Fantasy, Chrono Trigger, Warcraft, Warcraft II, Diablo, Starcraft, Red Alert, Age of Empires etc.
"This sounds really cool." I hear you say... Well, it is. And this tutorial will teach you the ropes.
Super Mario Brothers & Final Fantasy 2 [tileenginegoodness.html Return To Top]
What do I need to do this?
So I got you interested eh?
To actually be able to implement this, you are going to need to know whats what. To start, you want a data structure. I'm telling you, YOU WANT A DATA STRUCTURE. You can of course whip up your own engine without one, but that is not reccomended. A data structure will keep your engine well organized, easy to read and above all easier to code. In this tutorial we use an array to hold our map data, this will be seen later on in the [#whatcode code] section.
You will also need an atholgorithim to follow the mapping instructions given by the tile map (an array in our case), and lay down tiles onto the buffer accordingly. Now there are many ways to achive this, some more complex, speedier and just plain better than others. But this tutorial will emthasise on just one of them. The most simplistic one: Total redraw.
Total Redraw
Total redraw is probably the most common type of tile engine, although more slow than that of other types, total redraw has the large advantage of being very simple to implement. What total redraw basically does is blits a cirtain tile (given by the tile map array) to the buffer. The cirtain tile is put on to the buffer in the specified increments (decided on the size of the tile) to form a grid. This specific athalgorithim copys from left to right, up to down (as illustrated below).
How a our tile engine works The tile size we will use for this tutorial will be 32x32 pixels. Here is an example sprite that could be used in our game:
Now, when our tile map athalgorithim comes into play, and we use different tiles, we CAN come up with a final product that looks something like this:
An example blitted tile map So, you are going to need:
- A data structure
- A mapping atholgorithim
[tileenginegoodness.html Return To Top] What does the code look like? Ok, were now down to the raw code. Our code is divided up into two files for organization, main.cpp and map1.h. One for our main program and mapping athalgorithim and the other for our data structure (or tile map) . Sidenote: source comments are in blue.
main.cpp
// Tile engine
// THIS ENGINE ONLY REQUIRES ALLEGRO
#include <allegro.h>
#include "map1.h"
// variables
int endgame;
int x_p1 = 10;
int y_p1 = 280;
// Bitmap pointers
BITMAP *buffer;
BITMAP *player1;
BITMAP *tiles[8];
//---------------------------------------------------------
// INITIALIZATION FUNCTION
//---------------------------------------------------------
void init ()
{
// Install keyboard & timer
install_keyboard();
install_timer();
}
//---------------------------------------------------------
// DRAW BUFFER
//---------------------------------------------------------
// showBuffer copies the contents of the buffer bitmap to the actual screen
void showBuffer ()
{
// Blit to screen
blit (buffer, screen, 0, 0, 0, 0, 640, 480);
}
//---------------------------------------------------------
// INPUT
//---------------------------------------------------------
void input ()
{
// If up is pressed, move player up
if (key[KEY_UP])
y_p1--;
// If down is pressed, move player down
if (key[KEY_DOWN])
y_p1++;
// If left is pressed, move player left
if (key[KEY_LEFT])
x_p1--;
// If up is pressed, move player right
if (key[KEY_RIGHT])
x_p1++;
// If esc is pressed, exit
if (key[KEY_ESC])
endgame=1;
}
//---------------------------------------------------------
// DRAW MAP FUNCTIONS
//---------------------------------------------------------
void draw_map(BITMAP *buffer, int x1, int y1)
{
register int i1, j1;
// base level blitting
for (i1 = 0; i1 < 15; i1++)
{
for (j1 = 0; j1 < 20; j1++)
{
// draw tile at appropriate area by incrementing the loop and then adding quardinates
draw_sprite(buffer, tiles[map1[i1][j1]], (j1 * tilesize) + x1, (i1 * tilesize) + y1);
}
}
}
//---------------------------------------------------------
// INITIALIZE GRAPHICS
//---------------------------------------------------------
// initialise our graphics routnine
int initGraphics ()
{
set_color_depth (16);
if (set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0))
{
// generate an error message
// note: allegro_message can be used on any platform
allegro_message ("Error: Could not initialize graphics mode!");
return 0;
}
return 1;
}
//------------------------------------------------------------
// MAIN
//------------------------------------------------------------
int main (void)
{
// initialize allegro
allegro_init ();
// call initialization function
init();
// set background of text to clear
text_mode(-1);
if (initGraphics())
{
// create our buffer
buffer=create_bitmap (640, 480);
// load our image data
DATAFILE *tilesets = load_datafile ("tilesets.dat");
DATAFILE *sprites = load_datafile ("sprites.dat");
// assign your loaded tiles to data
tiles[0] = (BITMAP*)tilesets[0].dat;
tiles[1] = (BITMAP*)tilesets[1].dat;
tiles[2] = (BITMAP*)tilesets[2].dat;
tiles[3] = (BITMAP*)tilesets[3].dat;
tiles[4] = (BITMAP*)tilesets[4].dat;
tiles[5] = (BITMAP*)tilesets[5].dat;
tiles[6] = (BITMAP*)tilesets[6].dat;
tiles[7] = (BITMAP*)tilesets[7].dat;
player1 = (BITMAP*)sprites[0].dat;
while (endgame==0)
{
// check for input
input();
// draw our background
draw_sprite(buffer, (BITMAP*)sprites[1].dat, 0, 0);
// draw tile map
draw_map(buffer, 0, 0);
// display our text
textout(buffer, font, "Welcome to our tile engine!",10,10, makecol(10, 100, 10));
// draw our sprite
draw_sprite(buffer, player1, x_p1, y_p1);
// Show the buffer & clear the buffer
showBuffer ();
// clear buffer
clear(buffer);
}
}
// clean up allegro
allegro_exit ();
return 0;
}
END_OF_MAIN ();
map1.h
//MAP1 data
int tilesize = 32;
int map1[15][20] ={{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
,{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 3, 0, 0, 0, 0}
,{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 5, 0, 0, 0, 0}
,{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 5, 0, 0, 0, 0}
,{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 5, 0, 0, 0, 0}
,{0, 2, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 5, 0, 0, 0, 0}
,{0, 4, 6, 5, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 5, 0, 0, 0, 0}
,{0, 4, 6, 5, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 5, 0, 0, 0, 0}
,{0, 4, 6, 5, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 5, 0, 0, 0, 0}
,{0, 4, 6, 5, 0, 0, 0, 2, 3, 0, 0, 0, 4, 6, 6, 5, 0, 0, 0, 0}
,{0, 4, 6, 5, 0, 0, 0, 4, 5, 0, 0, 0, 4, 6, 6, 5, 0, 0, 0, 0}
,{0, 4, 6, 5, 0, 0, 0, 4, 5, 0, 0, 0, 4, 6, 6, 5, 0, 0, 0, 0}
,{0, 4, 6, 5, 0, 0, 0, 4, 5, 0, 0, 0, 4, 6, 6, 5, 0, 0, 0, 0}
,{0, 4, 6, 5, 0, 0, 0, 4, 5, 0, 0, 0, 4, 6, 6, 5, 0, 0, 0, 0}
,{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}};
Conclusion Congratulations, you now know the concept of a basic tile engine. REMEMBER: You can always adapt and change this code to suit your needs. For example say you want bigger tiles or a smaller tilemap etc., all you need to do is change the appropriate variables, and maybe some structure if nessesary to get your wanted results. Ok bye all, and... HAPPY CODING!
References: Tile Based Gold Tile Maps Some tile graphics grabbed from: Tiles
