Pixelate:Issue 11/Tile Engine Goodness

From Allegro Wiki

Jump to: navigation, search
Tile Engine Goodness
Original author: Nathaniel Sabanski
some mail
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.

Image:P11 Smb.gif Image:P11 Finfan2.jpg 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).

Image:P11 Tiles.gif 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:

Image:P11 Tree.jpg Image:P11 Tree.jpg Image:P11 Tree.jpg An example tile (1x, 2x, 3x)

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:

Image:P11 Completed.jpg 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

Personal tools