[Resolved] Something is wrong with my collision detection.

Started by
11 comments, last by tonemgub 10 years, 8 months ago

I only have a basic 2D platformer right now with a tile-based map.

My AABB collision check code is this


int check_collision_rect(Rect A, Rect B)
{
    //The sides of the rectangles
    int leftA, leftB;
    int rightA, rightB;
    int topA, topB;
    int bottomA, bottomB;
	

    //Calculate the sides of rect A
    leftA = A.x;
    rightA = A.x + A.w;
    topA = A.y;
    bottomA = A.y + A.h;

    //Calculate the sides of rect B
    leftB = B.x;
    rightB = B.x + B.w;
    topB = B.y;
    bottomB = B.y + B.h;

    //If any of the sides from A are outside of B
    if(bottomA <= topB)
    {
        return 0;
    }

    if(topA >= bottomB)
    {
        return 0;
    }

    if(rightA <= leftB)
    {
        return 0;
    }

    if(leftA >= rightB)
    {
        return 0;
    }

    //If none of the sides from A are outside B
    return 1;
}

Then this is how I use it


	Rect tile;
	tile.h = 64;
	tile.w = 64;	
	tile.x = 0;
	tile.y = 0;
	for(int i = 0; i < Map->tile_num; i++)
	{
		if(Map->tile[i]->type == 2) // Type 2 is a solid tile
		{
			if(check_collision_rect(player.pos, tile))
				exit(0);
		}
			
		tile.x += 64;
		if(tile.x >= LEVEL_WIDTH)
		{
			tile.x = 0;
			tile.y += 64;
		}
	}	

And here is my structure in case you guys feel it's important smile.png


typedef struct Rect {
	int h, w;
	float x, y;
	float prev_x, prev_y; // Previous positions
	float velX, velY; // Velocity for X and Y coordinate. 
} Rect;

What is happening? Well, nothing. The game should exit when I collide with the solid tile, but it's not detecting the collision no matter how much I move. I put the for loop directly after my code where I move the player to a new position.

EDIT: I think I posted this prematurely after wondering for hours what the problem was. I think it may be that x and y are floats which might be causing the problems.

Advertisement

if(bottomA <= topB)
{
    return 0;
}

if(topA >= bottomB)
{
    return 0;
}

if(rightA <= leftB)
{
    return 0;
}

if(leftA >= rightB)
{
    return 0;
}

//If none of the sides from A are outside B
return 1;

Edit: Hmm, interesting way of doing it.

How about this?


if(check_collision_rect(player.pos, tile))

That should be tile, should it not?

I've borrowed the code from http://lazyfoo.net/SDL_tutorials/lesson17/index.php, but it's also explained here http://www.gamedev.net/page/resources/_/technical/game-programming/collision-detection-r735.

I've used the code in my earlier game and it worked like a charm, but here...it does not.

Are you using standard Cartesian coordinates? For example, describe your origin position and which direction +X and +Y are.

For a standard Windows based window, the origin is in the upper-left with X growing towards the right edge and Y growing towards the bottom, which appears to be what your code assumes. However, if your coordinate system is standard Cartesian, your coordinate computations are "flipped" in Y.

Don't get this the wrong way, but why are you drawing at 2000 FPS? I get the heebeegeebees when I see games max out a whole CPU core just to draw the same static image 2000 times per second... :) You should enable VSync, or even better - only Update when something actually changes on the screen, or both.

I don't think the floats are a problem, because they will get casted implicitly to ints when needed and you will get a compiler warning about that so it doesn't go unnoticed, unless you turned those warnings off. So the actual values that are used in the calculations can only be off by 1.

One thing that i found out the hard way by looking for a bug for 2 days is and may be helpful to you:

Floats get rounded down when positive and rounded "up" to a higher value when negative.

So 4.56f will become 4.0f and -4.56f will become -4.0f.

But as long as you are not doing pixel-perfect collision detection that shouldn't be a problem. ( I did :) )

Anyways, here are a few things you can try to find your bug:

1. Make sure your collision detection works. To make absolutely sure I would write a small program that draws two rects, where one can be moved with the mouse or keyboard and then use check_collision_rect() to see if it works in every single possible constellation. You could set the color of the rects accordingly, red if they intersect, green if no. If that works perfectly then the bug is clearly somewhere else.

2. See if the tile you are colliding with is indeed solid or maybe you just forgot to set that attribute? Run your game and set a breakpoint at that section of code when your player should be colliding and step through the code and look at the variables. Is the type really 2 when it should be?

3. It could even be LEVEL_WIDTH that's causing problems, who knows :P If it's wrong your loop will wrap incorrectly and your player would never collide. Again use your debugger and while stepping through look at every variable possible if it has the value you would expect.

Learn to use the debugger and you will find bugs like this MUCH faster and easier. Remember you can set a breakpoint even while your program is running.

Oh any btw I think the bounding boxes should not be:

rightA = A.x + A.w;

but

rightA = A.x + A.w - 1;

Why? Let's say you have a rect that starts at 0,0 and is 10 pixels wide:

The width is 10, so rightA would be 0 + 10 = 10, is that correct? No :) The rightmost pixel really is 9 -> 0,1,2,3,4,5,6,7,8,9, that's 10 pixels wide.

Are you setting your tiles positions correctly?


//Assuming positions start with (0,0) in the top left corner of the window
tile.h = 64 + DistanceFromTop;
tile.w = 64 + DistanceFromLeft;	
tile.x = DistanceFromLeft;
tile.y = DistanceFromTop;

Unless of course I don't understand exactly how you are doing it.

You could also definitely refactor your code and redesign your collision detection system. Right now it seems to be a little weirder than it needs to be.

I don't think the floats are a problem, because they will get casted implicitly to ints when needed and you will get a compiler warning about that so it doesn't go unnoticed, unless you turned those warnings off. So the actual values that are used in the calculations can only be off by 1.

One thing that i found out the hard way by looking for a bug for 2 days is and may be helpful to you:

Floats get rounded down when positive and rounded "up" to a higher value when negative.

So 4.56f will become 4.0f and -4.56f will become -4.0f.

But as long as you are not doing pixel-perfect collision detection that shouldn't be a problem. ( I did smile.png )

Anyways, here are a few things you can try to find your bug:

1. Make sure your collision detection works. To make absolutely sure I would write a small program that draws two rects, where one can be moved with the mouse or keyboard and then use check_collision_rect() to see if it works in every single possible constellation. You could set the color of the rects accordingly, red if they intersect, green if no. If that works perfectly then the bug is clearly somewhere else.

2. See if the tile you are colliding with is indeed solid or maybe you just forgot to set that attribute? Run your game and set a breakpoint at that section of code when your player should be colliding and step through the code and look at the variables. Is the type really 2 when it should be?

3. It could even be LEVEL_WIDTH that's causing problems, who knows tongue.png If it's wrong your loop will wrap incorrectly and your player would never collide. Again use your debugger and while stepping through look at every variable possible if it has the value you would expect.

Learn to use the debugger and you will find bugs like this MUCH faster and easier. Remember you can set a breakpoint even while your program is running.

Oh any btw I think the bounding boxes should not be:

rightA = A.x + A.w;

but

rightA = A.x + A.w - 1;

Why? Let's say you have a rect that starts at 0,0 and is 10 pixels wide:

The width is 10, so rightA would be 0 + 10 = 10, is that correct? No smile.png The rightmost pixel really is 9 -> 0,1,2,3,4,5,6,7,8,9, that's 10 pixels wide.

About point 3, you are correct, but after fixing that...it made no difference, but it also makes the code loop around every tile over and over again each time tile.y += 64 which was a huge performance hit.

As for a debugger, GDB command line is slightly harder to use.

Are you using standard Cartesian coordinates? For example, describe your origin position and which direction +X and +Y are.

For a standard Windows based window, the origin is in the upper-left with X growing towards the right edge and Y growing towards the bottom, which appears to be what your code assumes. However, if your coordinate system is standard Cartesian, your coordinate computations are "flipped" in Y.

In order to move right X must be negative i.e X=-1 will move me one pixel to the right, Y=-1 will move me one pixel upwards.

Can you provide the following set of inputs?

Two rectangles you expect to intersect and two that you don't. Give the full struct values that you use.

Better yet, if you're not going to use GDB, provide a way to dump all the rectangle coordinate information into a log / stdout when you press a button.


if(bottomA <= topB)
{
    return 0;
}

if(topA >= bottomB)
{
    return 0;
}

if(rightA <= leftB)
{
    return 0;
}

if(leftA >= rightB)
{
    return 0;
}

//If none of the sides from A are outside B
return 1;

Edit: Hmm, interesting way of doing it.

How about this?


if(check_collision_rect(player.pos, tile))

That should be tile, should it not?

Wow thanks, this fixed it. It was so obvious, I guess I got confused from my first 2D game attempt a year ago.

This topic is closed to new replies.

Advertisement