SDL collisions

Hello! I have been working on collisions for my 2D game in SDL and I cannot get them to work smoothly. My collisions left and right are very smooth but when ever I move my character up or down and I hit something I get stuck. Any help on how I can improve my collision correction would be great!

My collision code: (dy and dx are essentially the character's velocity)

bool check_collision(float Aw, float Ah, float Ax, float Ay, float Bx, float By, float Bw, float Bh)
{
//The sides of the rectangles
int leftA, leftB;
int rightA, rightB;
int topA, topB;
int bottomA, bottomB;

//Calculate the sides of rect A
leftA = Ax;
rightA = Ax + Aw;
topA = Ay;
bottomA = Ay + Ah;

//Calculate the sides of rect B
leftB = Bx;
rightB = Bx + Bw;
topB = By;
bottomB = By + Bh;

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

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

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

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

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


void collisionDetect(GameState *game) {

for (int i = 0; i < 213; i++) {
if (check_collision(32, 32, game->obj1.x, game->obj1.y, game->wall[i].x, game->wall[i].y, game->wall[i].w, game->wall[i].h)) {
game->obj1.x -= game->obj1.dx;

}
else if (check_collision(32, 32, game->obj1.x, game->obj1.y, game->wall[i].x, game->wall[i].y, game->wall[i].w, game->wall[i].h) && game->obj1.dy != 0) {
game->obj1.y -= game->obj1.dy;
}
}
}
You need to check whether the rectangles overlap. How it is done see:

http://stackoverflow.com/questions/306316/determine-if-two-rectangles-overlap-each-other
You need to check whether the rectangles overlap

The code does that.

The problem is that the else condition will never be true. It requires that game->obj1 and game->wall[i] collide, but if they do, then the if part will be true and the else part won't be checked.

bool check_collision(float Aw, float Ah, float Ax, float Ay, float Bx, float By, float Bw, float Bh)

So you pass width, height, x, y of the first object, but you pass
x,y,width,height of the second object. I Can almost guarantee you that you'll mess up the call somewhere and pass both sets of data in the same order.

A much better way of doing this would be
bool check_collision(GameObject &obj1, GameObject &obj2)
Then call it like this:
 
if(check_collision(game->obj1, game->wall[i]))

This would require storing the width and height of game->obj1 instead of using magic constant 32, but that's probably a good idea anyway.
Thank you for the replies!

@dhayden, I implemented your suggestion and my collision functions are much simpler and neater.

I am still running into the issue however where when I hit either the top or the bottom of a block my character gets stuck until I release the keys while hitting a block to the left or right works perfectly. Any suggestions on how to fix this issue?
What about my comment regarding the calls to check_collision() and the fact that the else part is never excecuted?
I changed the else into another if statement so now both are checked, but that still leads to sticky collisions when colliding with the top and bottom of a block.

Both of my statements are pretty much the same which could be the issue? I just don't understand why one of them is working better than the other.

One thing that I notice is that when I change the code like this:
(switching the statements so that the y is changed before the x)

for (int i = 0; i < 213; i++) {
if (check_collision(game->obj1, game->wall[i])) {
game->obj1.y -= game->obj1.dy;
}
if (check_collision(game->obj1, game->wall[i])) {
game->obj1.x -= game->obj1.dx;
}

The character now moves smoothly colliding with the top and bottom of a block but now gets stuck when colliding to the left or right. I have also tried putting both of the offset expressions in the same if statement but that results into sticky collisions on every side.

Your code is doing the following:
- If colliding, try to cancel the y movement
- If still colliding, try to cancel the x movement

This means you can either cancel the y movement or both the y and x movement when colliding, which results in the sticking behavior. FOr your purposes, something like the following may work:
- If colliding, try to cancel y
- If still colliding, undo y cancel (re-add the Δy), and try to cancel x
- If still colliding, both were necessary so cancel y again
It's easier to handle each axis separately.

update the x position
if colliding
    move out of collision by modifying x

update the y position
if colliding
    move out of collision by modifying y


This might not be the most realistic method but if the game is updated quickly, so that the distance moved each update is not too big, you won't usually be able to tell the difference.
Last edited on
http://www.cplusplus.com/forum/general/43393/
has some good information.

http://www.cplusplus.com/forum/lounge/65258/
too if you read through a bit.
Thanks for the advice! Ill try to implement some of the suggestions to see if I can get it to work!
@Zhuge your suggestion is so close to working for me!

This is what I have right now:

void collisionDetect(GameState *game) {

for (int i = 0; i < 213; i++) {
if (check_collision(game->obj1, game->wall[i])) {
game->obj1.y -= game->obj1.dy;
if (check_collision(game->obj1, game->wall[i])) {
game->obj1.y += game->obj1.dy;
game->obj1.x -= game->obj1.dx;
if (check_collision(game->obj1, game->wall[i])) {
game->obj1.y -= game->obj1.dy;
}
}
}
}
}

With this, top and bottom collisions are smooth and going down on the left or right side of a block are smooth. However going up the left or right side of block is smooth briefly and then the character gets stuck again
Bump - I still am struggling to find a solution to the problem. If anyone has any more suggestions it would be a big help!
Can you fix your code tags? They are not displaying properly.

It looks like you are nesting all of your if statements but because of the formatting it is hard to tell. Why are you doing that?

You may also try Peter87's suggestion and see if that is sufficient for your physics.
Yes, sorry about that I'm just becoming familiar with this forum! And yes I am nesting all of my if statements. From your suggestion earlier that's what it sounded like I needed to do, sorry if I misinterpreted it.

Here is the corrected format:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void collisionDetect(GameState *game) {

	for (int i = 0; i < 213; i++) {
		if (check_collision(game->obj1, game->wall[i])) {
			game->obj1.y -= game->obj1.dy;
			if (check_collision(game->obj1, game->wall[i])) {
				game->obj1.y += game->obj1.dy;
				game->obj1.x -= game->obj1.dx;
				if (check_collision(game->obj1, game->wall[i])) {
					game->obj1.y -= game->obj1.dy;
				}
			}
		}
	}
}


I have tried Peter87's suggestion before although it results into my character glitch through the map everywhere. I have tried changing the signs of collision correction, but it does not seem to help. Here was my implementation of his suggestion in my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
	const Uint8 *state = SDL_GetKeyboardState(NULL);
	
	if (state[SDL_SCANCODE_A] || joystickLeft) {
		game->obj1.dx -= 1;
		if (game->obj1.dx < -3) {
			game->obj1.dx = -3;
		}
		for (int i = 0; i < 213; i++) {
			if (check_collision(game->obj1, game->wall[i])) {
				game->obj1.x += game->obj1.dx;
			}
		}
	}

	if (state[SDL_SCANCODE_D] || joystickRight) {
		game->obj1.dx += 1;
		if (game->obj1.dx > 3) {
			game->obj1.dx = 3;
		}
		for (int i = 0; i < 213; i++) {
			if (check_collision(game->obj1, game->wall[i])) {
				game->obj1.x -= game->obj1.dx;
			}
		}
	}

	if (state[SDL_SCANCODE_W] || joystickUp) {
		game->obj1.dy -= 1;
		if (game->obj1.dy < -3) {
			game->obj1.dy = -3;
		}
		for (int i = 0; i < 213; i++) {
			if (check_collision(game->obj1, game->wall[i])) {
				game->obj1.y -= game->obj1.dy;
			}
		}
	}

	if (state[SDL_SCANCODE_S] || joystickDown) {
		game->obj1.dy += 1;
		if (game->obj1.dy > 3) {
			game->obj1.dy = 3;
		}
		for (int i = 0; i < 213; i++) {
			if (check_collision(game->obj1, game->wall[i])) {
				game->obj1.y += game->obj1.dy;
			}
		}
	}

	if ((!state[SDL_SCANCODE_S] && !state[SDL_SCANCODE_D] && !state[SDL_SCANCODE_W] && !state[SDL_SCANCODE_A]) && (!joystickLeft && !joystickRight && !joystickUp && !joystickDown)){
		game->obj1.dx *= .8f;
		game->obj1.dy *= .8f;
		if (fabsf(game->obj1.dx) < .1f) {
			game->obj1.dx = 0;
		}
		if (fabsf(game->obj1.dy) < .1f) {
			game->obj1.dy = 0;
		}

	}

Could you edit the previous post with the incorrect code tags? There's a forum issue where having code tags like that breaks the page for certain browsers and prevents from posting replies without hacking via JavaScript (I had to go into the console and run thread191299.reply() to be able to reply).

No, I think your understanding is correct but my algorithm is incorrect (or at least extremely inefficient if you want to implement it correctly). I think it may work if you interpret my "is colliding" as checking the object against every wall, rather than a single wall, because as it is you may:
- cancel the Δy to move out of a ceiling and still be in a wall
- cancel the Δy again because you are inside the wall
- cancel the Δx to get out of the wall
which I certainly didn't intend for.

To be honest, if you want to keep your a box from colliding with walls I would do what Disch recommended in my previously linked thread and draw lines, checking for collisions between those velocity lines and the wall lines.
Wow that's interesting hahaha. I assume you would like Peter87's suggestion:

const Uint8 *state = SDL_GetKeyboardState(NULL);

if (state[SDL_SCANCODE_A] || joystickLeft) {
game->obj1.dx -= 1;
if (game->obj1.dx < -3) {
game->obj1.dx = -3;
}
for (int i = 0; i < 213; i++) {
if (check_collision(game->obj1, game->wall[i])) {
game->obj1.x += game->obj1.dx;
}
}
}

if (state[SDL_SCANCODE_D] || joystickRight) {
game->obj1.dx += 1;
if (game->obj1.dx > 3) {
game->obj1.dx = 3;
}
for (int i = 0; i < 213; i++) {
if (check_collision(game->obj1, game->wall[i])) {
game->obj1.x -= game->obj1.dx;
}
}
}

if (state[SDL_SCANCODE_W] || joystickUp) {
game->obj1.dy -= 1;
if (game->obj1.dy < -3) {
game->obj1.dy = -3;
}
for (int i = 0; i < 213; i++) {
if (check_collision(game->obj1, game->wall[i])) {
game->obj1.y -= game->obj1.dy;
}
}
}

if (state[SDL_SCANCODE_S] || joystickDown) {
game->obj1.dy += 1;
if (game->obj1.dy > 3) {
game->obj1.dy = 3;
}
for (int i = 0; i < 213; i++) {
if (check_collision(game->obj1, game->wall[i])) {
game->obj1.y += game->obj1.dy;
}
}
}

if ((!state[SDL_SCANCODE_S] && !state[SDL_SCANCODE_D] && !state[SDL_SCANCODE_W] && !state[SDL_SCANCODE_A]) && (!joystickLeft && !joystickRight && !joystickUp && !joystickDown)){
game->obj1.dx *= .8f;
game->obj1.dy *= .8f;
if (fabsf(game->obj1.dx) < .1f) {
game->obj1.dx = 0;
}
if (fabsf(game->obj1.dy) < .1f) {
game->obj1.dy = 0;
}

}

I hope that is somewhat readable/understandable. I will look into line collisions and see if that will work! Thank you for helping out so much, I really appreciate it.
Topic archived. No new replies allowed.