Advantage with tile map over LARGE bitmap, etc...
SonShadowCat

From what I know tile maps offer a memory advantage over using a large bitmap, how is this possible?

Ive read 2-3 articles about tile maps( 1 for java games) and I dont really get tile maps.

I mean how are they made in a bitmap, do you just open up MSPaint, make a file say 800x600 and divide it into the size tiles you want? Then you just chop them up and put them into an array?

Am I way off here?
Please help set me straight, becuase I really dont see the difference between using tile maps and a really big bitmap....

ty

23yrold3yrold

The idea is to eliminate repetition. Best example would be Super Mario Bros. Now, the first level is made up of maybe 12 different tiles (blocks, bricks, sky, bush, a few for pipes, question blocks, etc.). Now, instead if a 2D array of bitmaps, now you have a 2D array of ints, and each int represents a tile. Now when you draw the screen, wherever the map has a 2, you put a brick tile, where there's a 3, you put bricks, etc. So instead of one big bitmap with the whole level and lots of blocks, you just have one bitmap of blocks, and you blit it wherever you need it.

Hang on; I may have a simple demo here somewhere (you'd think there'd be a tilemap demo in allegro/examples ...)

Ah, here's the beginning of a Pixelate tutorial I was going to do once (may yet finish it one day). Incomplete, but shows the basics. I wish spellcaster's site was still up; that had good info ...

Tilemaps

I see a lot of tilemap format tutorials out there (and so will you, because I have a bunch of resources for you at the end). We won't be discussing that here. Well, not initially anyway. Everyone has a way to implement tilemaps, and one person's way may not work for another. I'm going to show you the concepts and theory behind tilemaps, so you can understand how they work. Then, I'll show you my way of coding it. Then, a few other ways I've seen it done which work well. Finally, some links to sites where you can learn more about tilemap structure and techniques, and I can assure you there's no shortage of those. In this manner, I hope that you won't merely accomplish the simple task of getting graphics to display in a grid on your screen (child's play), but to be able to write your own classes, file formats, structures, drawing techniques, personal preferences, etc. so wherever you go, you'll introduce yourself with a bow and a "Greetings .... I am .... (insert your name here) .... grand mas-tah tilemap codah .... this keyboard and mouse .... are a-deadly a-weapahns ....".

Tilemaps are very simple to code, and my only assumption here is that you know arrays and for loops. This tutorial is platform independant; the code examples I give are in Allegro, but that's because it's a pretty self-explanitory library of functions, and I'm sure you can port these examples to any platform with a minimum of fuss. I code C++, but I don't use anything fancy, so this is essentially C code.

Why use tilemaps?

Hmm, good question. Tilemaps are used for video game background, right? Why go through all this bother to design a class and stuff when we can just use one great big image? One blit and your done. Works for Resident Evil, works for Final Fantay VIII. Heck, it works for Abe's Exodus, and it's a platform game! Sure, that may seem easier to implement. But games like Final Fantasy have areas only two screens wide and high (at best). And Abe and RE don't scroll at all! Yet Super Mario Bros., a game some 15 or 20 years old, has areas that scroll for something like 40 screens? How'd they do that? All in the magic of tilemaps. And as we'll see, tilemaps do more than hold mere image data.

Remember when I said we'd look at how to implement tilemaps after we discuss theory? I lied; we need an implementation so I can demonstrate the theory. Keep in mind I'm organizing data like this to make it easier for you to learn; we'll look at infinitly better implementations later. First, we need to store our tiles. What's a tile? A square (or rectangular, depending on your tastes) bitmap, many of which are used to draw a background or environment. Every tile used for a particular background should be the same width and height so they fit together easily. The dimensions could be anything, but some sizes are easier to use than others. Common practice is to pick sizes that divide evenly into typical screen resolutions, and better yet, powers of 2. 8x8, 16x16, 32x32, 64x64, 20x15 and 40x30 are all perfectly fine. 8x8 is probably a bit small for most games, and 16x16 is nice for old NES games, but probably unsuitable for today's high resolution games. I generally use 32x32 myself. Anyway, for our discussion here, it doesn't matter what the size of the tiles are, only that they all have the same size.

Now go look at your Super Mario Bros. See the tiles? The entire game is comprised of tiles. Brick tiles. Block tiles. Question block tiles. Pipes. Clouds. The infamous flagpole. Any part of the background is a tile, all layed out in a grid. In fact, any given level is only made up of 20 or so tiles. And that's a major advantage of tiles; you can build a world with a handful of small tiles, which uses far less memory than one big background image. This also means you can make more levels, not just more efficient ones. Big graphics take up storage space as well as RAM. And finally, it takes a lot less time to make good looking tiles than it does to make good looking screen-sized bitmaps.

Code time

So, how to hold our tile data?

BITMAP*   tiles[20];

This is an array of 20 pointers-to-BITMAP. A BITMAP (no, I'm not shouting) is an Allegro structure for holding graphic image data. If your using some other graphics library, don't leave yet. This translates to any library easily enough; just substitute your own structure for BITMAP. Now then, this is our first tilemap essential, we need an array of structures which hold information on individual tiles. This needn't be done this way; there are other ways we'll see later. But this is probably the easiest to understand. Now we need to fill that array with something. Images, preferably. Yes, that will do nicely. We'll load a bunch of individual images into these slots:

tiles[0] = load_bmp("skytile.bmp",   NULL);
tiles[1] = load_bmp("cloudtile.bmp", NULL);
tiles[2] = load_bmp("grasstile.bmp", NULL);
tiles[3] = load_bmp("bricktile.bmp", NULL);
tiles[4] = load_bmp("blocktile.bmp", NULL);
..... /* etc. */

Those of you at home who wish to play along can draw 20 quick, small bitmaps named whatever you want and load them into the 20 elements of tiles[]. Don't bother being Picasso here; the sky tile is blue, the cloud tile is a white blur on blue, the grass is a green shape on a blue background (you may not be Picasso, but you can have a Blue period, right?).... just throw some pics together. And make however many you need; it doesn't have to be 20. For those of you new to Allegro, load_bmp() loads a bitmap (duh) given the filename of the image. NULL would be palette information when dealing with 8-bit color, but I'm a 16-bit kinda guy, so I don't use it. See Allegro's documentation for proper use of this function and any others I bring up.

And that's it. We have our image data. But how do we get it onscreen? And better yet, how do we map out the locations of our tiles onscreen (now you know why it's called a tilemap). First question second, second question first. We need a layout for our tilemap, so the computer knows what tile goes where. This is the second essential of tilemaps; we stored the individual tile information, now we need to store the tile layout. Time for another array, this time a 2 dimensional one:

int map[5][10] = {
   {0, 0, 1, 0, 0, 0, 0, 1, 0, 0},
   {0, 0, 0, 0, 1, 0, 0, 3, 0, 0},
   {1, 0, 3, 0, 0, 0, 3, 3, 1, 0},
   {2, 0, 3, 2, 2, 3, 3, 3, 2, 2},
   {4, 4, 4, 4, 4, 4, 4, 4, 4, 4}
}

Obviously we can have bigger tilemaps than that, but we'll use 5x10 as an example. Now look back at how I loaded my bitmaps. tiles[0] is a sky tile, right? So everywhere there's a 0 in map[][], there will be sky in the background. 1 will be a cloud, 2 will be grass, etc. From the look of map[][], this is a sky with some clouds, a floor made of blocks with some grass, a small stack of bricks and a brick staircase. Simple. The number in the layout corresponds to the element of tiles[] that goes there.

So, we have our images, and we know where they go. Better yet, the computer knows. But how do we get the computer to draw it correctly onscreen? Easiest thing in the world, m'lad:

for (int y = 0; y < 5; y++)
   for (int x = 0; x < 10; x++)
         blit(tiles[ map[ y ][ x ] ], screen,  0, 0, x * 32, y * 32, 32, 32);

Now that's pretty loaded for 3 lines of code, so let's step through it. The first two lines are pretty self explanitory; we want to access all elements of map[][], and the best way to do that is to cycle through it with 2 for loops. blit() is pretty loaded. For those of you who are total newbies, the term blit means to draw a bitmap onto something. Here, we blit to the screen, and if we're going to continue, I best spell out for you what blit() does. Here's the prototype, straight from Allegro's documentation:

void blit(BITMAP *source, 
          BITMAP *dest, 
          int source_x, 
          int source_y,
          int dest_x, 
          int dest_y, 
          int width, 
          int height);

OK, the first parameter is the image we want to draw. For our purposes, obviously we want to draw a tile. Look at the for loops again, we pass tiles[ map[ y ][ x ] ] as the first parameter. map[ y ][ x ] gets us the number of the tile we want, and we give it directly to tiles[]. So that's how we use map[][] to figure out what image in tiles[] we draw, and because the for loops go through map[][] in it's entirety, we draw every tile in map[][]. The second parameter is where the image is going. Here, we draw to the screen. The next two parameters are the coordinates that we want to start drawing from our tile. We're drawing the whole tile, so we pass 0, 0, which is the upper-left corner. The next part is interesting; the coordinates where we start drawing to. This is important; the tiles should be evenly spaced, matching up at the edges. But it's not hard; we just multiply the width of the tile by x and the height of the tile by y (note that x and y change every loop). So the first tile would be drawn at x * 32 = 0, then x * 32 = 32, x * 32 = 64, 96, 128, 160 ..... perfectly spaced! The final two parameters are the width and height we want to draw; again we want the whole tile to be drawn, so we just pass the width and height of the tiles. Here, I'm assuming the tiles are 32x32; change all the 32's in blit() to suit your tile's dimensions.

So that's it for drawing. We use two for loops to access every element in map[][], using that element to determine what image in tiles[] to draw. And we use the loop variables to determine where on the screen to draw the tiles.

SonShadowCat

holy crap dude, you either have alot of time on your hands or you love to help ^^

thx alot, im gonna read it all after im done watching the history channel( battle history of the US army, good stuff ^^)

thx again

Johnny13

long post~:P

Quote:

Brick tiles. Block tiles. Question block tiles. Pipes. Clouds. The infamous flagpole. Any part of the background is a tile

what happen if a big block is 5 tile widthx5 tile height,how it place to a 'tile'? need to split it? ???

Thomas Fjellstrom

some people will split up large items, but another way is to make the large things an 'object' on a sepertate layer, drawn over the tile layer.

SonShadowCat

It would make a good pixelate article, where are the links you promised at the beginning of it>? :P

I think I understand tilemaps slightly better now, meow

23yrold3yrold

I quoteth myself:

Quote:

Ah, here's the beginning of a Pixelate tutorial I was going to do once (may yet finish it one day). Incomplete, but shows the basics.

Anyway, GameDev has a lot of links to tilemap tutorials; check it out.

SonShadowCat

the evil that is gamedev....
havent been there is months...

23yrold3yrold

There's some good links here then ...

Thomas Harte
Quote:

thx alot, im gonna read it all after im done watching the history channel( battle history of the US army, good stuff ^^)

A program about US history? That should take all of five minutes!

Now, my constructive comments - many people who are not stuck with 1980's style tilemap hardware don't used fixed size tiles. Quadtrees are an obvious example, but they have limitations for this purpose. A very simple alteration you can make without having to think too hard is to include a 'skip' count in your tiles. Assuming you are moving left to right, after each tile is drawn you then don't draw for as many tiles as the skip count says. For a standard tile map, the skip count would always be 0.

If you wanted to include a tile that was actually sized nxm tiles, you'd link its bitmap to the top left tile it covers, and leave a skip count of n-1, then for m-1 rows below, you'd fill in an n skip count on the preceeding tile.

I hope this makes sense . . .

Johnny13
Quote:

1980's style tilemap hardware

Nintendo?;)

and what if Third big-block overlay the Second,the Second overlay the First??:o

Thomas Harte

I agree, Nintendo's hardware designers are stuck in the 1980s when it comes to 2d hardware. Obviously systems that demonstrate good designs, such as the Amiga & Lynx passed them by.

Quote:

and what if Third big-block overlay the Second,the Second overlay the First??

Obviously you can get some quantity of overlap using this quick hack, but higher tiles will always appear below lower ones, and ones further to the left will always appear below those further to the right. However, it is just a quick hack for people who insist on sticking to tilemaps but want a bit of extended flexibility in terms of large tiles - its perfectly adequate if you aren't going to have overlap.

Something I forgot to mention - it is also useful to give the tiles that are ordinarily skipped on the line where a large bitmap is drawn a negative skip value, pointing to the tile before them that actually holds a bitmap. That way you can regain what you are meant to be drawing even after reducing the tile set to those visible at the current screen offset. Of course, on lines that have a skip because of a large tile on a previous line, just use positive offsets to the next graphical tile.

SonShadowCat

You guys lost me...

Anyway...I tried making a little tilemap thing but it crashes when I start it. I pretty much copied and pasted what you posted chris, heres the code

1#include "allegro.h"
2#define TILE_W 32
3#define TILE_H 32
4 
5int main()
6{
7 allegro_init(); // Initialize allegro
8 install_keyboard(); // Install the keyboard routines
9 install_timer(); // Install the timer routines
10 
11 set_color_depth(8); // Set the color depth to 8
12 set_gfx_mode( GFX_AUTODETECT, 800, 600, 0, 0); // Set the screen size to 800x600
13 
14 DATAFILE *Images=load_datafile( "c:/C++/Allegro/Tilemap/Tiles.dat"); // Load the data
15 // File containing the tiles
16 
17 BITMAP *Tiles[4]; // Create an array of bitmaps to hold the tiles
18 for( int a=0; a<4; a++) // Load all the tiles into the array
19 {
20 blit( (BITMAP*)Images[a].dat, Tiles[a], 0, 0, 0, 0, TILE_W, TILE_H); // blit the tiles into
21 // The correct space
22 }
23 
24 int Map[5][10]={ {0, 0, 2, 2, 0, 0, 0, 2, 2, 0}, {0, 0, 2, 0, 0, 0, 0, 2, 0, 2},
25 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {3, 3, 3, 3, 0, 3, 3, 3, 0, 3},
26 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; // Create a map
27 
28 BITMAP *Buffer=create_bitmap( 800, 600); // Create a buffer that is 800x600 big
29 clear_bitmap( Buffer); // Clear the bitmap
30 
31 for( int y=0; y<5; y++) // Go down each set of tiles on the y-axis
32 {
33 for( int x=0; x<10; x++) // Go right each set of tiles on the x-axis
34 {
35 blit( Tiles[Map[y][x]], Buffer, 0, 0, x * TILE_W, y * TILE_H, TILE_W, TILE_H); // Blit the tilemap
36 // To the buffer
37 }
38 }
39 
40 blit( Buffer, screen, 0, 0, 0, 0, 800, 600); // Blit the buffer to the screen
41 
42 while( !key[KEY_ESC])
43 {}
44 
45 return 1;
46}
47END_OF_MAIN();

Thomas Harte

Here is the mistake :

  BITMAP *Tiles[4];  // Create an array of bitmaps to hold the tiles 

   for( int a=0; a<4; a++)  // Load all the tiles into the array
   { 
      blit( (BITMAP*)Images[a].dat, Tiles[a], 0, 0, 0, 0, TILE_W, TILE_H);  // blit the tiles into the correct space
   }

You haven't allocated any memory to 'Tiles'. The simple solution would be to do instead this :

  BITMAP *Tiles[4];  // Create an array of bitmaps to hold the tiles 

   for( int a=0; a<4; a++)  // Load all the tiles into the array
   { 
      tiles[a] = create_bitmap(TILE_W, TILE_H);
      blit( (BITMAP*)Images[a].dat, Tiles[a], 0, 0, 0, 0, TILE_W, TILE_H);  // blit the tiles into the correct space
   }

But I suspect you are copying from the datafile to different bitmaps so as you can use hardware acceleration where available? In which case you want to look at gfx_capabilities and possibly use one of create_video_bitmap or create_system_bitmap.

SonShadowCat

silly me ><

I wasnt even thinking of hardware acceleration...

23yrold3yrold
Quote:

Now, my constructive comments - many people who are not stuck with 1980's style tilemap hardware don't used fixed size tiles. Quadtrees are an obvious example, but they have limitations for this purpose. A very simple alteration you can make without having to think too hard is to include a 'skip' count in your tiles. Assuming you are moving left to right, after each tile is drawn you then don't draw for as many tiles as the skip count says. For a standard tile map, the skip count would always be 0.

I'd thought of that (I was going to call it RLE tilemaps :) ) and also of multisized tiles, but it seems more trouble than it's worth. I mean, I want to be able to access these things for collision detection and maybe their "types" (if it's ice, you slide, etc.) plus if it's a big map and I'm off on the right side, it's a pain to go through the whole lookup to find where to start blitting. There's probably some advantage that justifies not taking the simple 2D array approach, but it works, it's easy, it's fast, so screw it. :)

Korval

What is the difference between a single 128x128 tile and 16 32x32 tiles (made from sub-BITMAPs of the large one)? Any tile should be an even multiple of the smallest tile, at the very least, so that they fit together correctly. Therefore, why not simply break the tile up into smaller pieces? This can be done either at load time, or even by the tilemap editor tool, so that the artist can work with tilesets that contain different sized tiles (all are even multiples of a small one). That should work pretty reasonably. The artist can continue the fiction that a tiles can be of varying sizes, while the program does work behind the scenes to ensure that the tilemap engine works correctly.

Thomas Harte
Quote:

There's probably some advantage that justifies not taking the simple 2D array approach

There are plenty of advantages. For example, one time I experimented with a game idea where the entire level was deformable - it would stretch and depress based on where you distributed weight. I had some good ideas of where to take such a game, but as with most things, just didn't bother. Anyway, thats just one 2d platform type game which would almost certainly not be helped by me suddenly deciding I wanted to use a tile map.

I personally would opt for a 2d polygonal representation regardless of the project, there is just so much more interactivity you can include that way, with procedurally placed vertices, making the positions of various things functions of other things, and so on.

23yrold3yrold
Quote:

There are plenty of advantages. For example, one time I experimented with a game idea where the entire level was deformable - it would stretch and depress based on where you distributed weight. I had some good ideas of where to take such a game, but as with most things, just didn't bother. Anyway, thats just one 2d platform type game which would almost certainly not be helped by me suddenly deciding I wanted to use a tile map.

Sounds like a tech demo more than a game. How often would you do that? 99 times out of a hundred, a 2D tilemap is fine IMHO.

Quote:

I personally would opt for a 2d polygonal representation regardless of the project, there is just so much more interactivity you can include that way, with procedurally placed vertices, making the positions of various things functions of other things, and so on.

Agreed, but presently I feel lazy and went with bitmasks. My math is too sucky for something like that :(

Bob
Quote:

Any tile should be an even multiple of the smallest tile

Yes, but if you push it a little further: every tile should be a multiple of the smallest possible tile, the 1x1 tile. At this point, you have arbitrary sized tiles :)

dudaskank
Quote:

1980's style tilemap hardware

This style is the same nowadays, only more colors.
Look at Donkey Kong Country 1/2/3, Super Mario World 2, Final Fantasy 4/5/6, Chrono Trigger, etc...
The SNES are tilemap

Quote:

Any tile should be an even multiple of the smallest tile

These games uses this technique.

Korval
Quote:

Yes, but if you push it a little further: every tile should be a multiple of the smallest possible tile, the 1x1 tile. At this point, you have arbitrary sized tiles

Yes, but you're also very slow, as each pixel is being drawn with a blit call. A tilemap, at that point, becomes a palatted image.

Thomas Fjellstrom

maybe not :) you could expand that arbitrary tile size idea into a way to do some really nice zooming, that will work with any (4:3, 1:1, 16:9, etc) screen ratios.

Thomas Harte
Quote:

Sounds like a tech demo more than a game.

Then you must be stuck in a routine world of samey games.

Quote:

How often would you do that?

Probably just once. But then I'd only do any thing just once. This is a cause and effect style question. Are there lots of 2d tilemap/static scrollers nowadays (i.e. since it stopped being a hardware necessity for programmers of limited systems) because every programmer has a supremely limited imagination, or because there are so many tutorials?

Actually, seeing the number of identikit RPGs on here, it might just as well be the former as the latter.

Quote:

> 1980's style tilemap hardware
This style is the same nowadays, only more colors .... The SNES are tilemap

The SNES is hardware of the 80's, but lets not quibble. My point is that if you look at a well designed 2d powerhouse machine, such as the Atari Lynx, and a badly designed one such as the GameBoy Advance, you quickly see the difference.

On the Atari Lynx, you load a particular register with the address of your frame buffer, then you build a linked list of some sprites in memory (anywhere in memory, this is not a sprite list in the Nintendo sense), and you ask the hardware to draw those sprites with the provided scaling parameters. It draws them to your frame buffer and returns. Then maybe you send some more. Eventually you stop and flip buffers (double, triple or as many as you like), and if you limit yourself to the possibilities of tilemaps, then you certainly manage it all between one vsync period and the next. Or, if you liked, you could write to the frame buffer directly.

On the GameBoy Advance, you either get no help with the display at all, in a frame buffer mode, or you get hard wired tilemap modes, which can only be used for that purpose, and for which there is a single static sprite list in memory, but with restrictions such as no more than 8 sprites per scanline, and sprites are always drawn in reverse list order. The 8 that are drawn are the first to be found in the list. Luckily the GBA gets away with it by having a really excellent CPU which can very much 'do the business' in the frame buffer mode, but the actual 2d hardware is a pathetic joke. If you don't believe me, notice the way people say the 2d is about the same as the SNES, but the SNES was designed up until the very last minute to be NES compatible, so is highly based on that hardware, and that hardware was designed to be nothing but super cheap. There isn't a RAM chip in the box.

Notice that the Lynx is still the only handheld that can accelerate polygon drawing, as a result of its sprite functionality being so open. I won't go into the details though, since they aren't really relevant . . .

Korval
Quote:

Then you must be stuck in a routine world of samey games.

No, he's got a valid point. What does it bring to the game besides graphics?

Quote:

Are there lots of 2d tilemap/static scrollers nowadays (i.e. since it stopped being a hardware necessity for programmers of limited systems) because every programmer has a supremely limited imagination, or because there are so many tutorials?

Or, perhaps, because there are precious few benifits of "dynamic" 2D games? Besides the potential graphical attractiveness of "dynamic" 2D, what is the benifit of it? What are the potential gameplay benifits?

Not only that, how do you go about building a level made of "dynamic" things? This isn't a simple tilemap editor anymore; this is very specialized code here. How do artists draw content for "dynamic" 2D?

Personally, I think 2D graphics would benifit more from bump-mapped tilemaps/sprites than any so-called dynamic objects. The gameplay doesn't appear to need "dynamic" content.

SonShadowCat

o.O O.o
I have no idea what you guys are talking about -.-

I am in the process of making a "maze" type level using my new found tile map skills :) pixel perfect collision and all ^^

23yrold3yrold

So you got it working? Cool 8-) Toldja it was easy ;)

Quote:

Then you must be stuck in a routine world of samey games.

So games like Solar Jetman, Super Mario 3, Final Fantasy VI, Chrono Trigger, Symphony of the Night, Skullmonkeys, Yoshi's Island, Startropics, MegaMan X4, Battletoads, Zelda III .... these are all the same? :P

Here's one for you; my game objects have a variable called footing, which is a pointer to terrain of some kind, be it a tilemap, moving platform or whathaveyou. I can make a few seperate tilemaps for a whole level and have them move independantly of each other, say bobbing in the ocean or something. Or use callbacks for scripting things like extending walkways or blocking doors. Dynamic, and I'm not stuck "snapping to grid" like I might if I did RLE tilemaps. Maybe we use 1980 algorithms because they just plain got it right in 1980. ;D

Thomas Harte
Quote:

No, he's got a valid point. What does it bring to the game besides graphics

You see no change to gameplay from being able to do things like dynamically rotate and deform parts of the world? Or being able to procedurally define vertices? Even in a static world, you think it would be just as easy to define a rolling hill top by drawing all the different tiles as it would be just to put down a patch with bezier edges and flag it as being grass? What if you wanted a game that procedurally generated hilltops?

You see no benefit to being able to work in a mathematically valid way with normals? Games such as Sonic which do 'real physics' on tilemaps have restrictions on tile shapes and require a programmer or a tile creation system to additionally create a second lookup of angles at every edge pixel.

Quote:

So games like Solar Jetman, Super Mario 3, Final Fantasy VI, Chrono Trigger, Symphony of the Night, Skullmonkeys, Yoshi's Island, Startropics, MegaMan X4, Battletoads, Zelda III .... these are all the same?

That isn't a far comparison. If the hardware can only do tilemaps, then you are forced to adapt your ideas to working on a tilemap, and to come up with increasingly clever ways of working around the tile map. If you're on a system that does not inherently use a tilemap, such as the PC, there is no such need to limit or reduce your ideas.

As an aside, speaking as someone who has never had a NES (they didn't do great business in Europe - they were easily outsold by the Master System, and most people bought computers instead anyway), what is Solar Jetman like? If you've played Jetman and Jetpac, is it as good as them?

SonShadowCat

o.O!!

Edit: My program is screwing me over, instead of moving the player in a straight line it goes in diagonals ><

I want to try to figure this out before I post it here, maybe ill be smart for 10 minutes and find out whats wrong -.-

edit2: With only 1 hour to go before I am forced out of my house I ask that you all help me ^^

here is the code for a little program that I made that lets you walk underground through "tunnels" till you reach the ultimate bottom. The whole area is visible( this will be changed)

this is the code for my main source file

#SelectExpand
1#include "allegro.h" 2#include "Player.hpp" 3#define TILE_W 32 4#define TILE_H 32 5 6int main() 7{ 8 allegro_init(); // Initialize allegro 9 install_keyboard(); // Install the keyboard routines 10 install_timer(); // Install the timer routines 11 12 set_color_depth(8); // Set the color depth to 8 13 set_gfx_mode( GFX_AUTODETECT, 800, 600, 0, 0); // Set the screen size to 800x600 14 15 DATAFILE *Images=load_datafile( "c:/C++/Allegro/Tilemap/Tiles.dat"); // Load the data 16 // File containing the tiles 17 18 int trans; 19 20 BITMAP *Tiles[4]; // Create an array of bitmaps to hold the tiles 21 for( int a=0; a<4; a++) // Load all the tiles into the array 22 { 23 Tiles[a] = create_bitmap(TILE_W, TILE_H); // Create bitmaps to blit the tiles to 24 blit( (BITMAP*)Images[a].dat, Tiles[a], 0, 0, 0, 0, TILE_W, TILE_H); // blit the tiles into 25 // The correct space 26 } 27 28 int Map[18][25]={ {0, 0, 0, 2, 2, 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0}, 29 {0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 2, 0, 2, 2, 0}, 30 {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}, 31 {1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1}, 32 {1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1}, 33 {1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1}, 34 {1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1}, 35 {1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1}, 36 {1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1}, 37 {1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1}, 38 {1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1}, 39 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 3, 3, 3, 1, 1, 1, 1, 1, 1}, 40 {1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 1, 1, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1}, 41 {1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1}, 42 {1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1}, 43 {1, 1, 3, 3, 3, 3, 3, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1}, 44 {1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1}, 45 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; 46 // Create the map 47 48 BITMAP *buffer=create_bitmap( 800, 600); // Create a buffer that is 800x600 49 clear_bitmap( buffer); // Clear the bitmap 50 51 BITMAP *background=create_bitmap( 800, 600); // Create a background buffer that is 800x600 52 clear_bitmap( background); // Clear the bitmap 53 54 BITMAP *foreground=create_bitmap( 800, 600); // Create a foreground buffer that is 800x600 55 clear_bitmap( foreground); // Clear the bitmap 56 57 for( int y=0; y<18; y++) // Go down each set of tiles on the y-axis 58 { 59 for( int x=0; x<25; x++) // Go right each set of tiles on the x-axis 60 { 61 blit( Tiles[Map[y][x]], foreground, 0, 0, x * TILE_W, y * TILE_H, TILE_W, TILE_H); // Blit the tilemap 62 // To the buffer 63 } 64 } 65 66 for( int y=0; y<18; y++) // Go down each set of tiles on the y-axis 67 { 68 for( int x=0; x<25; x++) // Go right each set of tiles on the x-axis 69 { 70 blit( Tiles[0], background, 0, 0, x * TILE_W, y * TILE_H, TILE_W, TILE_H); // Blit the tilemap 71 // To the buffer 72 } 73 } 74 75 int mask_color = bitmap_mask_color(foreground); // Find the translucent color 76 int white = makecol(255,255,255); // Color to change 77 78 for(int i=0; i<foreground->h; i++) // Go through enture height of bitmap 79 { 80 for(int j=0; j<foreground->w; j++) // Go through entire width of bitmap 81 { 82 if(trans=getpixel(foreground, j, i) == white) // If white is found 83 { 84 putpixel(foreground, j, i, mask_color); // Change the translucent color 85 } 86 } 87 } 88 89 blit( background, buffer, 0, 0, 0, 0, 800, 600); // Blit the buffer to the screen 90 masked_blit( foreground, buffer, 0, 0, 0, 0, 800, 600); // Blit foreground to buffer and skip 91 // Translucent pixels 92 blit( buffer, screen, 0, 0, 0, 0, 800, 600); // Blit the buffer to the screen 93 94 cPlayer *Player=new cPlayer; 95 96 while( !key[KEY_ESC]) 97 { 98 Player->Draw_Player( foreground); // Draw the player 99 Player->Move( foreground); // Move the player and check for collision detection 100 101 masked_blit( foreground, buffer, 0, 0, 0, 0, 800, 600); // Blit the foreground to the buffer 102 vsync(); 103 masked_blit( buffer, screen, 0, 0, 0, 0, 800, 600); // blit the buffer to the screen 104 } 105 106 return 1; 107} 108END_OF_MAIN();

now here is the code for my player header file

#SelectExpand
1class cPlayer // This is the player class 2{ 3 public: // The following code is public 4 5 cPlayer(); // The constructor 6 7 void Move(BITMAP *foreground); // The function that moves the player 8 9 bool Collision_Detection(BITMAP *foreground, int direction); // The collision detection routine 10 11 void Draw_Player(BITMAP *foreground); // The function that constantly draws the player 12 13 private: // The following code is private 14 15 short unsigned loc_x; // The x coordinate of the player 16 short unsigned loc_y; // The y coordinate of the player 17 short unsigned old_loc_y; // The last y coordinate of the player 18 19 short unsigned jump_limit; // How high the player can jump 20 short unsigned jump; // How much the user has jumped 21 22 BITMAP *Player; // The bitmap that holds the player 23}; 24 25cPlayer::cPlayer() // The constructor 26{ 27 loc_x=SCREEN_W/2; // The player starts off in the center of the x axis 28 loc_y=0; // The player starts off at the top of the screen 29 old_loc_y=loc_y; // The old y coordinate is the same as the current one 30 31 jump_limit=10; // The player can jump 10 pixels 32 jump=0; // The player hasnt jumped yet 33 34 Player=create_bitmap( 10, 10); // Create a bitmap that is 10x10 35 clear_bitmap( Player); // Clear the bitmap 36 circlefill( Player, 4, 4, 4, makecol( 255, 0, 0)); // Make a red circle the player 37} 38 39void cPlayer::Move(BITMAP *foreground) // the function that moves the player 40{ 41 42 bool move; // To check if we can move or not 43 44 if( key[KEY_RIGHT]) // If the user presses the right arrow key 45 { 46 if( move=Collision_Detection( foreground, 1) == false) // If there is no collision detection 47 { 48 loc_x+=3; // Move to the right 3 pixels 49 } 50 } 51 52 if( key[KEY_LEFT]) // If the user presses the left arrow key 53 { 54 if( move=Collision_Detection( foreground, 0) == false) // If there is no collision detection 55 { 56 loc_x-=3; // Move to the right 3 pixels 57 } 58 } 59 60 if( key[KEY_UP]) // If the user presses the up arrow key 61 { 62 if( move=Collision_Detection( foreground, 2) == false) // If there is no collision detection 63 { 64 if( jump<11) // If the user hasnt reached the jump limit 65 { 66 loc_y--; // Increase the y coordinate 67 jump++; // Increase the height that has been jumped 68 } 69 70 if( jump>9) // If the player has reached the limit 71 { 72 loc_y++; // Decrease the y coordinate 73 jump--; // Decrease the height that has been jumped 74 } 75 } 76 } 77 78 if( move=Collision_Detection( foreground, 3) == true) // If there is no ground 79 { 80 loc_y++; // Increase the y coordinate 81 } 82} 83 84bool cPlayer::Collision_Detection(BITMAP* foreground, int direction) // The collision detection routine 85{ 86 int trans; // This will check the color 87 88 if( direction==0) // If the direction is left 89 { 90 if( trans=getpixel( foreground, loc_x-1, loc_y)!=0) 91 { 92 return true; 93 } 94 } 95 96 if( direction==1) // If the direction is right 97 { 98 if( trans=getpixel( foreground, loc_x+1, loc_y)!=0) 99 { 100 return true; 101 } 102 } 103 104 if( direction==2) // If the direction is up 105 { 106 if( trans=getpixel( foreground, loc_x, loc_y-1)!=0) 107 { 108 return true; 109 } 110 } 111 112 if( direction==3) // If the direction is down 113 { 114 if( trans=getpixel( foreground, loc_x, loc_y+1)!=0) 115 { 116 return true; 117 } 118 } 119} 120 121void cPlayer::Draw_Player(BITMAP *foreground) // Draw the player 122{ 123 blit( Player, foreground, 0, 0, loc_x, loc_y, 10, 10); // Blit the player to the foreground 124}

23yrold3yrold

You'll have to be a bit more specific about the error. He moves diagonally ... so if you push right, he moves up and right? Also remember you should be checking two tiles; at any time there's a 31 in 32 chance the player has one foot on one tile and the other foot on another tile. Check both before you fall. It also might be wise to make your array of tiles a simple struct:
<code>
struct CTile
{
BITMAP* image;
bool walkable;
};
<code>
Now you can check the walkable value of any tile you hit; if it's false, you can pass through it; if it's true, you stop.

BTW, how much of your engine works?

Quote:

You see no change to gameplay from being able to do things like dynamically rotate and deform parts of the world?

For your examples, I'd still use a tilemap for the majority of the level, and make a game object (class CPlatform or something) for the more dynamic parts. No biggie ;) Also, levels on the SNES could rotate too (Contra III, Castlevania 4, the intro to Super Metroid ....)

Quote:

what is Solar Jetman like?

Well, the gameplay mechanics are like Asteroids, but with gravity and huge planetary levels to explore. You have to run around them and collect artifacts, resources, parts, money and haul them back to your mother ship with a tracktor beam. You can upgrade your Jetpod, or run around without it when it blows up ;) It's a very cool game.

Thomas Harte
Quote:

> . . . dynamically rotate and deform parts of
> the world?

For your examples, I'd still use a tilemap for the majority of the level, and make a game object (class CPlatform or something) for the more dynamic parts. No biggie

So, it is fair to claim that a tilemap is not sufficient for your needs.

Quote:

Also, levels on the SNES could rotate too (Contra III, Castlevania 4, the intro to Super Metroid ....)

The SNES can rotate an entire tilemap layer, or none of a tilemap layer. It cannot rotate part of a tilemap layer. But that is besides the point - this isn't a conversation about the limiting effects of the SNES hardware, but one about how I feel a lot of people select the tilemap as the representation for their game by default, even though it is not the best representation for them.

What if I wanted a room with that old cliché, walls that move in? A tilemap would not be sufficient, and I'd have to use sprites, which would require either I code that room as a special case, or much improved collision routines over those that would suffice for the normal platform interations with enemies.

What about something more interesting - say two rooms, connected by a piece of string over a pulley? I'd have to use different layers for each room, and either bodge a piece of string together with sprites, or write some non-tilemap code. So again, purely a tilemap is not sufficient.

How about if I wanted a cavernous cave complex, where new passageways could be instantly blasted in the walls? Well, if it were a tilemap, I'd have to severely limit the possibilities for new passageway shapes.

Because a tilemap is nothing more than a bitmap with reduced redundancy (and greatly increased draw costs when talking about adding arbitrary graphics) for repetitive graphics, it is true that any game can be produced using a tilemap. However, most people can come up with various games where just drawing to a bitmap is a better plan. There is a reason that people said Elite on the NES, or that sand level of Earthworm Jim 2 couldn't be done with tilemaps - they simply aren't the best kind of structure for that sort of work, and getting the hardware to work around them was non-trivial.

Finally, most people read a little more into the term 'tilemap', and associate them with limited collision detection routines that don't solve for when collisions happen, but for when the did and you missed it already, with a combination of the tile level map itself and endless hacks for player/world collision detection & usually with very dodgy physics that just about make gravity appear correct, but no more. This is just something to take into account before you all try and recommend a 'tilemap' for everything 2d.

On the Jetman issue, I'm old enough to have read the Jetman comic strip in Crash! magazine, so I have a continuing fond spot for the guy. I'll probably try and seek Solar Jetman through emulation in the next few days, is it purely a NES thing, or is there some Megadrive or SNES version with halfway decent graphics?

23yrold3yrold
Quote:

The SNES can rotate an entire tilemap layer, or none of a tilemap layer. It cannot rotate part of a tilemap layer. But that is besides the point - this isn't a conversation about the limiting effects of the SNES hardware, but one about how I feel a lot of people select the tilemap as the representation for their game by default, even though it is not the best representation for them.

For most of the examples you give, a tilemap is just fine "for a game" until you find you need something more flexible "for a small piece of a level", while a tilemap is just fine for the other 90% of the game ;). For your blowing holes in walls example, a tilemap or big bitmap would do well, depending on how you came at it. Can't speak for the Earthworm Jim 2 sand level; I have never had opportunity to play it. But I'll assume the rest of the game was tilemaps, if the first was any indication ;)

BTW, Solar Jetman is NES only AFAIK. There could be some Jap-only Super Famicon release I don't know about, but I doubt it.

Thomas Harte
Quote:

For most of the examples you give, a tilemap is just fine "for a game" until you find you need something more flexible "for a small piece of a level",

And what if my imagination for level design is not myoptic and I need something more flexible for all my "small pieces of levels"? What if I just want a camera that zooms inward and outward?

It seems to me that you are willing to admit that there are things that cannot be accomplished with a tilemap, but since you personally can't offhand think of any games that would do them quite frequently you write them off. You do realise that not everybody thinks the same?

Quote:

For your blowing holes in walls example, a tilemap or big bitmap would do well, depending on how you came at it.

A bitmap is not a tilemap, so using one would demonstrate that a tilemap is not good enough. Therefore, if you feel a tilemap would be sufficient, tell me how, in the general case, I could support something as simple as permanently adding a line segment - i.e. an equivalent to the Allegro 'line' - to my tilemap, without it becoming a very inefficient data structure, with especial reference paid to the bounds of current hardware acceleration. To remind you :

Quote:

Can't speak for the Earthworm Jim 2 sand level; I have never had opportunity to play it. But I'll assume the rest of the game was tilemaps, if the first was any indication

The whole thing was tilemaps, it being yet another unimaginative platform game, sold like most commercial titles principly on hype and secondarily on gameplay.

23yrold3yrold
Quote:

It seems to me that you are willing to admit that there are things that cannot be accomplished with a tilemap, but since you personally can't offhand think of any games that would do them quite frequently you write them off. You do realise that not everybody thinks the same?

But of course. :) But most games take place in a 99% static environment. I can look over my fairly large collection of games, leaf through a few EGM's and see it everywhere. Sure there are games like (I think the name is) Red Faction, where you can blow holes in the 3D level, and the level "re-writes" itself to put a big hole where you just fired your rocket. IIRC, that was the game's big selling point, and recieved very lackluster reviews. It was a tech demo first and gameplay second (I can say that for more games than I'd like nowadays).

Quote:

tell me how, in the general case, I could support something as simple as permanently adding a line segment

Personally, I'd just add a CLine object to my list of game objects. 20 lines of code.

As for the first Earthworm Jim, I thought it was pretty imaginative. The lightless level, the bungie jumping, riding around on a hamster, For Pete's Sake, etc. Tilemaps don't really restrict one's imagination.

Thomas Harte
Quote:

Personally, I'd just add a CLine object to my list of game objects. 20 lines of code.

From which two conclusions flow :

  • tilemaps are not sufficient

  • you are unwilling to be drawn into a genuine conversation
  • Quote:

    Tilemaps don't really restrict one's imagination.

    So you believe that if I go away and arbitrarily invent a 2d game, I will later discover that all my ideas are catered for by tilemaps? All your examples to date have been games that were forced to use tilemaps from the start and all ideas were conceived with that knowledge.

    23yrold3yrold
    Quote:
    • tilemaps are not sufficient

    Odd line of reasoning. Tilemaps aren't suficient for bullets or enemies either. So? Just as with the line, I'll use something else.

    Quote:

    So you believe that if I go away and arbitrarily invent a 2d game, I will later discover that all my ideas are catered for by tilemaps?

    No, but so far, I have, and what small examples you've given aren't enough to convince me otherwise. Thanks for your input though ;)</li>

    SonShadowCat

    and the post counts keep getting higher!

    23yrold3yrold

    Not mine. I better stop posting before I bottom out ....

    Thomas Fjellstrom

    No! you should keep going till you have -3000 posts ;)

    23yrold3yrold

    Quiet. You're supposed to be jumping a shark or something. ;)
    (actually, now that I think about it, that's rather sick .....)

    Thomas Fjellstrom

    Well... That does sound more fun than talking about your post count ;) so OK, I suppose I can get an extra shark for you so you don't feel left out ;D

    23yrold3yrold

    No, no, that's quite all right. You go ahead without me; I'll stay behind and watch movies on your 110 foot TV.

    Thomas Fjellstrom

    hehe... I'll tell you one thing, It wasn't Shaq's foot I used to measure the TV ;)

    SonShadowCat

    about my problem, im still stuck

    I dont get it, I wrote my code so that there WOULD be collision detection but there aint any ><

    the ball just goes straight down ><
    and the ball leaves a trail behind it, I tried clearing the buffer and foreground but the trail is still there

    (look at above code for reference) anyone got any ideas?

    Gabhonga

    1. your jump code is pretty screwed up ;-)

          if( jump<11)  // If the user hasnt reached the jump limit 
          { 
            loc_y--;  // Increase the y coordinate 
            jump++;  // Increase the height that has been jumped 
          } 
     
          if( jump>9)  // If the player has reached the limit 
          { 
            loc_y++;  // Decrease the y coordinate 
            jump--;  // Decrease the height that has been jumped 
          }
    

    when looking at this a bit closely and thinking about it, you should see why your player is moving kind of strange...try having a variable to decide whether the player is jumping up or down, and check this before adjusting the players heighth...i.e.

    1static int jumping = 0; //indicates wether the player is thrusting up or just standing or falling
    2static int jump; //like your linear counter style jump...of course gravity would look better ;-)
    3//...
    4//later in code, when checking input...:
    5//...
    6//first we check wether the player is falling down...
    7//wich could also happen because he's just walked down an edge!
    8if(jumping==0) //is the player supposed to be standing on ground?
    9{ if(Collision_Detection( foreground, DOWN) == false ) //erm...so that there's no collision
    10 { jumping=2; } //player walked off edge and is now falling...
    11 else if( key[KEY_UP] )
    12 { jumping=1; //mark the player as jumping up!
    13 jump=11; //may jump up 11 pixels
    14 }
    15}
    16if(jumping==1)
    17{ if(Collision_Detection(foreground,UP)==true) //boinked head into wall??
    18 {jump=0; jumping=2;} //mark player as falling
    19 else if(jump) { locy--; jump--; } //else move the player up a little further...
    20}
    21if(jumping==2) //falling down...
    22{ if(Collision_Detection(foreground,DOWN)==true)
    23 { jumping=0; } //hit ground, now standing
    24 else loc_y++; //or fall a little deeper..
    25}

    try out this trick...better would of course be to implement velocities and a kind of gravity, that looks smoother.

    about the trail: are you clearing the buffer you blit all your stuff on every frame?

    well hope this fixes some of your problems...

    SonShadowCat

    ohhhh, pretty code, thx

    Yes, I am clearing the buffer every frame

    Gabhonga

    duh...I've looked at your code again, and now see what you're doing wrong...
    in your main function you should change some little bits:

    1. clear your buffer thingie, i.e. by blitting background over it...

    2. masked_blit the foreground onto it (or, what would be better, have your tileset drawing routines here wich you've used to setup your foreground BITMAP)

    3. NOW draw the player onto the buffer, not onto foreground!

    4. sync and blit the buffer to screen.

    this should fix your trail problem :-)

    however, your collision detection also still seems a little improvised to me, and as this doubles up with your movement routines, there's still a little work to do to implement basic features...for example, like it's now you may run slopes down, but not up!!

    [eDiT] OoOps I made a mistake in the jumping code above!! like i've presented, the player would hover in mid-air when he finished floating up or hit a wall :D;D:D

    hmm, to correct this...

    //...change this...
    
    { if(Collision_Detection(foreground,UP)==true) //boinked head into wall?? 
    
    //into this!
    
    { if((Collision_Detection(foreground,UP)==true) || (jump==0))
    

    as silly as I am I forgot about that case when I wrote it up here :-b

    SonShadowCat

    ok there is no trail now, another problem popped uo from that

    since my circle doesnt cover the whole bitmap assigned to it I decided to masked_blit it to the buffer, but that causes the circle to now move... do I just keep it the way it was before?

    edit: Why the hell isnt my collision detection function working? I know its bad but shouldnt it still work?

    Thread #181827. Printed from Allegro.cc