2D platformer and hit detection

Started by
15 comments, last by Dark_Oppressor 14 years, 7 months ago
Well, here's the deal. I have some experience now with SDL, and have made some fairly small 2D projects. Aside from that, I am still a bit of a newbie. I have a function all made up that I use for simple hit detection, and that has been great thus far. However, now I am running into a snag. I now have a large world map, which is navigated by the player. There are also levels that the player can enter, which are sidescrolling 2D platformer types. My problem is this: How on earth do I handle hit detection for everything? First of all, what is a good way to let the game know my character is on the ground, or against a wall? Do I need to add in hundreds of hitboxes by hand? This is what I started to do, but it seems that there must be some FAR better approach that I simply cannot come up with. And then I need to detect whether or not my character has hit an enemy, or whether an enemy has hit him. I am a bit overwhelmed, and am hoping there is some relatively simple solution. For enemies, I figured I would make each enemy type a class. Each class would have a hitbox variable. Then, I could make an array of whatever enemy type I need a few (or a bunch!) of, and then have the player's hit detection function check each class in each array using for loops. Each enemy class could also have its own hit detection function included, and then another for loop could run each class in each array's hit detection function to see if they hit the player. I hope the above paragraph makes some sense. That is what I have come up with on my own, and I must admit I don't know a whole lot yet. Any light anyone could shed would be greatly appreciated.
Advertisement
Quote:For enemies, I figured I would make each enemy type a class. Each class would have a hitbox variable. Then, I could make an array of whatever enemy type I need a few (or a bunch!) of, and then have the player's hit detection function check each class in each array using for loops.


Sounds like you've pretty much got it. You have each enemy store its current position and collision rectangle, and each frame you just loop over every enemy and check against the player's collision rectangle.

With regards to the collisions with scenery: it depends on how all the backgrounds, etc, are stored. If you're using tiles then you can define some tiles as 'impassable' and the rest as 'passable' (or equivalent). Then you can just check against all the impassable tiles. Otherwise you'll have to create or make use of some form of level editor to define by hand where all the walls are, and check against all of them in the game. There are optimisations you can make to reduce the number of checks, but you probably don't need to worry about them at this stage.

EDIT: I hope I've understood your problem correctly!
Quote:Original post by Dark_Oppressor
First of all, what is a good way to let the game know my character is on the ground, or against a wall? Do I need to add in hundreds of hitboxes by hand? This is what I started to do, but it seems that there must be some FAR better approach that I simply cannot come up with.

You could generate those hitboxes from other, easier to create, data. Like, as shaolinspin already suggested, a tilemap.

Quote:For enemies, I figured I would make each enemy type a class. Each class would have a hitbox variable. Then, I could make an array of whatever enemy type I need a few (or a bunch!) of, and then have the player's hit detection function check each class in each array using for loops. Each enemy class could also have its own hit detection function included, and then another for loop could run each class in each array's hit detection function to see if they hit the player.

Sounds like code duplication to me. All those enemies would have a hitbox and the same collision check function, so why not put that into a single base class? In fact, if your enemies only differ in data (different sprite, different walking speed, etc.) then you might as well just write one class for them.
Create-ivity - a game development blog Mouseover for more information.
I'm in the middle of doing the same thing myself

At the moment my levels are images (ie not tile-based) and a simple level editor lets me drag either a rectangle collision box for walls, floors etc and a triangular one for slopes over each respective landscape feature in the image

the level editor when writing the data to an xml file splits the level into vertical sections

the game detects which section the player is in (is player's x co-ords between the start and end x values for that section) and only runs through the collision test loops for the collision boundaries in that section (each section is a node in an array with a vector of rectangles or triangles associated with it), just thought it'd worth mentioning that since you said you had large levels, might just speed things up a bit since you're not testing everything in the level against everything else each frame
Ok, great! So it sounds like I actually have a clue when it comes to the enemies. Captain P, I am already making classes for each enemy type, and I guess making just one class is probably all I really need to do, I'll change that. The class has a collision detection function in it, and that is what I was trying to say in my first post.

So that part is starting to sound quite doable. I'm still a bit worried about the level part, though. Mantrid, are you saying you made a little level editor for your engine? That is an interesting idea. What do you all think? Which is the better approach, tiles or non-tiles?

One more question: If you make a level using tiles, the actual level is "drawn" in your code, and does not exist in any image file, right? (Other than the individual tiles, obviously)
Quote:So that part is starting to sound quite doable. I'm still a bit worried about the level part, though. Mantrid, are you saying you made a little level editor for your engine? That is an interesting idea. What do you all think? Which is the better approach, tiles or non-tiles?

One more question: If you make a level using tiles, the actual level is "drawn" in your code, and does not exist in any image file, right? (Other than the individual tiles, obviously)


Well, the 'better' approach depends on what you want to do in your levels. For platformers, tiles are probably the most-used approach, because it allows you to create a great variety of shapes, and vast levels, with only a few graphical elements.

There are tile editors out there, which you could use to create levels in your game if you don't mind a bit of extra coding to create the functions to load them in.

You are right about the last point; the level is assembled and drawn on-the-fly from the individual tile graphics and a tile map. Using a single image for the level would use up a huge quantity of memory for anything other than small levels (i.e. the size of a few screens).
Quote:Original post by Dark_Oppressor

So that part is starting to sound quite doable. I'm still a bit worried about the level part, though. Mantrid, are you saying you made a little level editor for your engine? That is an interesting idea. What do you all think? Which is the better approach, tiles or non-tiles?

One more question: If you make a level using tiles, the actual level is "drawn" in your code, and does not exist in any image file, right? (Other than the individual tiles, obviously)


yeah with tile-based you might have say 1 image of a set of blocks like in mario, and a seperate data file just saying which squares in the level grid to draw this one block to, so it looks like you've drawn loads of platforms etc but in reality it's just the same image being cloned as it were

so like:

111111111111111
100000000000001
100000000000001
111100001111111
100000000000001
111111111111111

could be your level grid in a tile-based level, where 1 is a block that a player can collide with (0 could be empty space), so you've got a ground, ceiling and walls there with two platforms, all using the same single block graphic so you're only storing that one tiny image in the memory instead of an image of the entire level

it all depends on the design of your game, my level design is basically making a level out of photographs of local landmarks so i had to be able to adapt collision to predefined terrain, but the theory of grouping a level into sections if you have lots of things that can collide with each other still stands (ie if you have player bullets, enemy bullets, player and enemies who can all collide with a large level)
Ok, I think I'm starting to grasp all of this. It doesn't sound quite as scary now :-)

Another question: I know a fair amount of C++, but not all of it (for instance, I have tried on numerous occasions to figure out what the heck pointers do, without success). If I may refer you to the C++ language tutorial located at http://www.cplusplus.com/doc/tutorial/, I know everything up through Classes (II), except for pointers and dynamic memory. Thus far, this has not been a problem for me, but I am curious if it will eventually become one. As far as I can tell from thinking it all through, I should be able to write this current game with the knowledge that I have.

I have a partially completed roguelike that I started some time ago, and it had a little text file like the one you show, Mantrid. I guess a graphical tile-based level is about the same thing. I think that is the way I want to go. It definitely sounds doable.
you can get away with not knowing about classes, inhertance etc. for a small enough game (for smaller games they just make things a lot easier and cleaner) but i think pointers would be really useful just to have a sit down and get out of the way first, once you get an idea they're really not that complicated

as it says on the tin, they just point to stuff.

when you send something to a function, eg

void dostuff(float number1, int number2)

number1 and number2 aren't actually sent to the function per se, but a copy of them is created just for that function each time it is called, and destroyed after the function ends.

so if you wanted to actually change number2 within the function you could send a pointer to 2, so the function is dealing with the original number2 you're sending

also, you're sending a float, which means at one point in our program there are two floats, whereas it's "cheaper" to send a pointer to the float, since a pointer is smaller in size than a float, so you can do that

it is incredibly useful for passing pointers to large arrays or structs so you're not cloning a massive struct and all its data each time you wanna do something to it, means if you definitely do NOT want anything within that struct (etc) to change then use const to ensure it isn't modified.

so i'd definitely suggest learning a thing or two about pointers even if you're only spending a day or two on them, if you can't get your head round that tutorial google for a few different ones, some tutorial series are great but one bit just doesn't do it the way you'd like, so it's always good to google round other tutorials to say if anyone else has a better way of explaining it to you, then when you go back to the first one you'll understand it
About pointers, when I went from knowing how to use pointers to understanding the true nature of pointers, was when I started coding for Gameboy Advance. There, you don't have any operating system between you and the hardware, so all the RAM, cartridge ROM, control registers and stuff are just accessed directly by memory address.

For example, RAM starts at address 0x02000000, so you can just say

char *point = (char*)0x02000000;
*point = 50;

And now you've written the value 50 to the byte at memory address 0x02000000. It turns out all a pointer is, is a variable that contains a memory address.

Pointers themselves are 32 bits (4 bytes), and are basically the same as an unsigned int, regardless of the type that the pointer points to. The type only matters when you go to dereference it or add/subtract to it. In the example, the pointer points to a char, which is a 1 byte signed integer. So when you dereference it and write the value 50, it only changes one byte in memory. But if it was a pointer to a float, which is 4 bytes, then dereferencing would change 4 bytes in memory.

So, as Mantrid says, pointers are good for passing large structs to functions (just send the 32-bit address where the struct is in memory, rather than making a copy of all the variables in it), and for modifying the original struct (since the function has the address where the original is, it can then write to it).

This topic is closed to new replies.

Advertisement