platformer tutorial/guide in c
_jagged

Hello everyone, I'm interested in making a simple tile based 2d platformer in the style of ghouls n ghosts. But i'm having trouble finding information on how to begin. I've found tutorials on youtube that use c++ but nothing using plain c. I know I could probably adapt the info from one of these c++ tutorials into c, but they all tend to go straight into classes and inheritance and all the garbage of c++ that i'm not interested in.

Does anybody know of a tutorial or guide that could at least point me in the right direction? I know enough allegro and c to make simple shmups that constantly scroll, with parallax backgrounds and animations but I have no idea how to set up a tile based map, make tiles solid etc...

Cheers!

MikiZX

Possibly these pages can help you a little (please note I've never used these and they are mostly Allegro5 related):

https://github.com/dradtke/allegro_tiled
https://wiki.allegro.cc/index.php?title=Tilemap_Example
https://wiki.allegro.cc/index.php?title=Allegro_tile_maps

DanielH

Try yourself

*This is a basic example, from memory*

#SelectExpand
1//1. determine how many tiles will fit on your display at one time 2#define GRID_WIDTH 40 3#define GRID_HEIGHT 30 4 5//2. determine cell size 6#define CELL_WIDTH 32 7#define CELL_HEIGHT 32 8 9//3. determine how big the overall map is 10//will it scroll horizontally or vertically or both 11#define MAP_WIDTH 400 12#define MAP_HEIGHT 30 // same as grid height if not scrolling vertically 13 14int camera_x = 0 15int camera_y = 0 16int map[MAP_WIDTH * MAP_HEIGHT]; 17 18// for this example I'm using an array of bitmaps for tile 19// ALLEGRO *tile[]; 20void draw_map 21{ 22 int i, j, x, y, sx, sy, c; 23 24 x = camera_x / CELL_WIDTH; 25 y = camera_y / CELL_HEIGHT; 26 sx = camera_x - (x * CELL_WIDTH); 27 sy = camera_y - (y * CELL_HEIGHT); 28 c = map[x + (y * MAP_HEIGHT)]; 29 30 // draw grid height + 1 31 for (j = 0; j <= GRID_HEIGHT; ++j) 32 { 33 // draw grid width + 1 34 for (i = 0; i <= GRID_WIDTH; ++i) 35 { 36 // draw tile at pos 37 al_draw_bitmap(tile[c], 0, 0, (i * CELL_WIDTH) - sx, (j * CELL_HEIGHT) - sy); 38 } 39 } 40}

All you have to do is adjust camera_x and camera_y. You can adjust by pixel amount or whole tile size;

What I do is put player in the middle. If the player crosses an imaginary boundary then adjust the camera to counter movement;

example: The map above is for a 640x480 grid. You can put boundary at 80 and 560;

if (player_x < 80)
{
    camera_x -= (80 = player_x);
    if (camera_x < 0) camera_x = 0; // keep in check
    player_x = 80;
}

if (player_x >= 560)
{
    camera_x += (player_x - 560);
    if (camera_x >= (MAP_WIDTH - (GRID_WIDTH * CELL_WIDTH))) camera_x = (MAP_WIDTH - (GRID_WIDTH * CELL_WIDTH)));
    player_x = 559;
}

RmBeer2

It has no secret, it's just about printing a mesh of images based on a double index vector.

_jagged

Thank you all for your replies, I think there's enough here to get started. I'm now realising I actually know less about game design than programming. It's just
the map that's moving.. damn. So, does the player just float a certain tile height? as in there's no coding of solid/ethereal for the ground? If the ground is two tiles deep then the player's y position (at feet) is window height - 2* tile height?

MikiZX

If you were making a game as 'Ghosts 'n Goblins' then movement would be somewhat easier to make as 'Ghosts 'n Goblins' does not have slanted surfaces to walk over like 'Ghouls n Ghosts' does. Slanted surfaces would slightly increase complexity of your collision detection algorithm. It is 'collision detection algorithms' that help us define which part of the map is solid or ethereal.
Once you would have a working collision detection algorithm in your game, adding the ground and making it solid would be just a matter of placing the ground tiles in the tilemap and letting the collision detection algorithm do the work of detecting solids for us.

An oversimplified version of collision detection you could use follows but please note that it is just a suggestion of a direction you might take:
For a simple 'Ghosts 'n Goblins' type of movement you could come close to a working solution by simply casting your player's floating point coordinates to an 'integer' and use that to look what is located at player's position in the tilemap.
In this scenario:
-a tilemap is basically just a two dimensional array of tiles.
-when drawing your player's character on screen you would multiply its position by tiles' size in pixels
To detect if the player is walking over something, when player's position is (12.34, 4.32) then one would lookup the tilemap at location [12][4+2] - provided your character is two tiles high (thus the '+2'). If the tilemap lookup returns a value that indicates that there is a 'tile' at [12][6] then the player is standing on a tile - if the lookup returns 'empty space' then your character would be falling (so simply increase its Y coordinate by 0.1 while the lookup returns 'empty space').
Similarly, if your character was moving to the right then you would be looking up tilemap values that are one tile to the right of your character's position.
Though please note this is just a suggestion - you would need to adjust this suggestion to a solution that fits your project.

I'm not sure if I'm explaining this well. If I should explain better please let me know.
And likely someone else on the forum might propose a more optimal way of doing all this.

And, as an advanced programming task, and possibly not needed for your project, if you were to expand the collision detection further you could look into using Box2D which is a library that can help with collision detection problems - though you would need to write a parser for your tilemap that would transcribe your tilemap into objects that Box2D understands and likely learning a new library might not be something you've really planned on.

P.S. For ease of understanding collision detection I would say that 'Ghouls n Ghosts' maps actually have two layers (one layer is the actual game-field that is considered when collision detection is done, and the second layer is drawn behind and is just a background - a decorative one, not considered when collision detection is processed). The enemies in that game would be taken in account when collision detecting though the player-enemy collision is likely done using rectangle-rectangle collision detection algorithm. To understand collision better try searching on the Internet using these keywords: "2d platformer collision detection algorithm"

Mark Oates

There are no tilemap classes in AllegroFlare yet for me to share, so my apologies on that.

Right now I have 7 different tile map projects, each implementation is different so there are a lot of approaches that can work depending on your needs. I'm in the process of consolidate them all into a single one, Tileo being the most consolidated one so far. It does lack a simple renderer like the one DanielH sketched out. I should probably add that next before anything else.

The more advanced renderer is a mesh renderer (the tiles in the tilemap are placed on a single 2 dimensional 3d mesh of squares, each square is a tile in the map).

One version also uses a custom ALLEGRO_PIXEL_FORMAT which includes normal data for each pixel, so dynamic lighting effects can be added via shaders. I have a couple screenshot of that:

{"name":"612202","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/1\/a\/1a581cc2b8faecb4db7cb3c0646e0fec.png","w":894,"h":552,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/1\/a\/1a581cc2b8faecb4db7cb3c0646e0fec"}612202
{"name":"612204","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/7\/0\/70ca9136a765391ebb09477e56d5fa15.png","w":1052,"h":638,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/7\/0\/70ca9136a765391ebb09477e56d5fa15"}612204
{"name":"612201","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/9\/5960af68c4b4aa46ead54c140ed4fe80.png","w":1037,"h":632,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/9\/5960af68c4b4aa46ead54c140ed4fe80"}612201
{"name":"612200","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/0\/80fe41b0ac13721b7648318c74a5dc6a.png","w":902,"h":615,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/0\/80fe41b0ac13721b7648318c74a5dc6a"}612200
{"name":"612198","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/c\/9\/c9bc8901833a3e58c579d6623a6cc41f.png","w":1017,"h":510,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/c\/9\/c9bc8901833a3e58c579d6623a6cc41f"}612198

_jagged said:

So, does the player just float a certain tile height? as in there's no coding of solid/ethereal for the ground?

Collision for tile maps is another topic.

Edgar Reynaldo

Hey baby, nice tiles. :D Wanna collide?

DanielH

612213

I spent a couple hours and made this code as a simple example of what I was talking about.

I don't do plain c, so I tried my best not to overcomplicate it.

Any questions, just ask.

This is a simple horizonally scrolling tilemap. There are layers &background, solid, foreground, items, etc). At the moment there is only drawing layers and no collision checking. Only boundary checking. The "player" is the red rectangle. With the left and right keyboard buttons you can move left and right. The camera will scroll accordingly.

The tile bitmap is pretty empty at the moment, but enough for example.

You might have to adjust location of where to load the tiles.

_jagged

Thanks everyone! DanielH, I created something similar to what you posted only my version is much more simple using primitive rectangles. I now have boundary detection and left/right collision detection, and a jump.

Thanks again.

Ariesnl

This is a GREAT resource : http://www-cs-students.stanford.edu/~amitp/gameprog.html#tiles

I hope this is in a book someday 8-)

Chris Katko

Mark Oates: That's great! I am all about some 2-D bump mapping.

Audric

There is one thing that sometimes takes time to realize / re-invent, when you see how platform games work : Some tiles have to count as "soft" platforms.

A completely "solid" tile will resist player movement from every direction. As a result, the player always stays 'outside' of it:

  1. Horizontally, you can not walk into it : if your feet, head or body would overlap, the horizontal movement is halted.

  2. If you jump, your head is not allowed to enter it. The jump will be halted, and you will start falling instead.

  3. It will "break your fall" if you're falling into its top edge. As long as your character's feet are resting on it, you don't fall.

"soft" platforms only apply ruler n°3). Most platform games have such. If the player has no jump-down controls that lets them fall through them voluntarily, the level design has to take it into account (there are holes or designated ladders, or no need to go back down).

Edgar Reynaldo

All you really need is Line Segment vs Line Segment collision detection. Then you can do anything you want.

DanielH

Yes, but that's not "simple"

Edgar Reynaldo

I coded my own AABB intercept routines, which account for velocity and acceleration. Once you know a collision has taken place, you can do anything with it. Stop movement. Bounce. Let it pass through, whatever.

And velocity based line segment collision is not hard. It's adding in things like rotation and acceleration that make it hard.

_jagged

I'd just like to thank you all again for the info in this thread, I appreciate all the responses even if I haven't acknowledged everyone, I have absorbed your wisdom. This whole endeavour of game making has been overwhelming. Making games is damn hard. I've battled with so many collision detection and response problems over the last week. But I feel they've really strengthened my understanding of C.

Thanks everyone.

DanielH

Here is a "simple" point collision for the code above.

This uses a mask for collision. I created a bitmap the same size as the tiles. Black is open, White is closed.

#SelectExpand
1 bool collide(int x, int y) 2 { 3 static int tx = 0; 4 static int ty = 0; 5 static int sx = 0; 6 static int sy = 0; 7 static int px = 0; 8 static int py = 0; 9 static int value = 0; 10 static ALLEGRO_COLOR p; 11 12 tx = (x >> TileShiftX); 13 ty = (y >> TileShiftY); 14 sx = x - (tx << TileShiftX); 15 sy = y - (ty << TileShiftY); 16 17 // returns value of cell at specified x, y, layer 18 value = getValue(tx, ty, LayerSolid1); 19 20 px = (value % TileAcross) << TileShiftX; 21 py = (value / TileAcross) << TileShiftY; 22 23 p = al_get_pixel(MaskBitmap, px + sx, py + sy); 24 25 return (p.r != 0 || p.g != 0 || p.b != 0); 26 } 27 28// call it 29bool hit = collide(x - camera.x, y - camera-y);

Thread #617985. Printed from Allegro.cc