2D basic collision detection help

Started by
3 comments, last by Goran Milovanovic 11 years, 10 months ago
Hey there guys, im looking for a very basic simple 2D collision code for C++
This is a method i have at the moment

bool check_collision( SDL_Rect A, SDL_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 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;
}

now this works, but I'm trying to make a 2D side scroller, now how can i use this code to just check lets say the sides? so he cant move left or right, how can I check to see if he hits his head so it tells yVel to be set to 0, and how can i check to see if there is a collision at the bottom, so he doesn't fall through the blocks, now this collision detection at the moment is fine for a monster, but not movement, could someone help :)
thank you so much

Ok i have just changed my character class so it has 2 collision boxes,

height = 81;
width = 33;
headH = 83;
headW = 28;
I think i have some stuff working now, but when i move my character speeds up, so for instance 1,2,3 speed, once it hits 3 it will stop at 3, but if i get to 3 then hit a block my character will get stuck inside, any idea on how i can resolve this?

this code is in my update method

if(moveRight == true && collision.check_collision(A,B))
{
moveRight = false;
pos.x = tempX;
}
if(moveLeft == true && collision.check_collision(A,B))
{
moveLeft = false;
pos.x += tempX;
}
if(falling == true && collision.check_collision(A2,B))
{
falling = false;
pos.y = tempY;
yVel = 0;
}
Advertisement
Hi Canvas.

In another thread, I posted this pseudo code, for doing what you are asking. Specific to your 2nd question (about getting stuck in the block), see the udpdate function, and how I move the player back the last velocity given, to ensure it's not in a state of colliding.


// Speed player moves left or right
#define MOVEMENT_SPEED 10.0f

// initial velocity given to player when he jumps
#define JUMP_VELOCITY 20.0f

void Player::HandleInput()
{
if (LeftIsPressed()) {
this.xVelocity = -MOVEMENT_SPEED;
}
else if (RightIsPressed()) {
this.xVelocity = MOVEMENT_SPEED;
else {
this.xVelocity = 0.0f;
}

// Only jump if we're not already jumping or falling
if (JumpIsPressed() && this.OnGround) {
this.yVelocity = -JUMP_VELOCITY;
}
}

// defines amount to increase downward velocity every frame
#define GRAVITY_FORCE 4.0f

void Player::Update()
{
// Apply downward force to player
this.yVelocity += GRAVITY_FORCE;

// Move the Player
this.xLocation += this.xVelocity;
this.yLocation += this.yVelocity;

// Check we've collide with something above or below us
if (CheckCollisionY()) {
// move us back to previous location and Stop Y Velocity
this.yLocation -= this.yVelocity;
this.yVelocity = 0.0f;
this.OnGround = true;
}
else {
this.OnGround = false;
}

// Check if we've collided with anything on our left or right

if (CheckCollisionX()) {
// move us back to previous location and Stop X Velocity
this.xLocation -= this.xVelocity;
this.xVelocity = 0.0f;
}
}


As for code to check if you hit your head or your feet, I would suggest 1st moving the object only by the y velocity, and see if it's collided. If it has, then you know it's hit it's head or feet, and you can set yVel to 0. Then do the same for applying the xVel, and check the collision. If it has collided after moving horizontally, then you can set the xVel to 0. This doesn't quite fit with the code given below, but you should get the jist of it.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

I seem to have gotten something working, but at the moment he still gets a little stuck in the blocks, but for me to fix that, im just telling the player object to take the collision's X or Y axis and copying them but with some modifications, i will post again if something goes wrong, which it probably will :(

also just a quick question, i have a player that can move left and right, but what i would like to happen is, if the player holds right it will increase the speed to move right, if the player then holds down left, it will first decrease the speed to 0 then start to move left, and vice versa, how would i implement that? also this is my code so far, it seems ok, just want to see if you think its alrite :)

if right arrow key is pressed moveRight becomes true
same for left arrow and moveLeft

if(moveRight == true && moveLeft == false)
{
if (xVel <= maxSpeed)
{
xVel = int(0.40 * speedTimes);
speedTimes +=1;
}
}
if(moveRight == false && moveLeft == true)
{
if (xVel >= (maxSpeed * -1))
{
xVel = int(-0.40 * speedTimes);
speedTimes +=1;
}
}
if(moveRight == false && moveLeft == false)
{
if(xVel >0)
{
speedTimes = 1;
xVel = -0.20;
}
if(xVel <0)
{
speedTimes = 1;
xVel = +0.20;
}
}
if(moveRight == true && moveLeft == true)
{
if(xVel >0)
{
speedTimes = 1;
xVel = -0.20;
}
if(xVel <0)
{
speedTimes = 1;
xVel = +0.20;
}
}
Hey there! If your code seems to be working ok, that's the most important thing - but I think I see a few nasty bugs (and a few ways to clean up the code, while I'm at it).


if(moveRight == true && moveLeft == false)
{
if (xVel <= maxSpeed)
{
xVel = int(0.40 * speedTimes);
speedTimes +=1;
}
}
if(moveRight == false && moveLeft == true)
{
if (xVel >= (maxSpeed * -1))
{
xVel = int(-0.40 * speedTimes);
speedTimes +=1;
}
}


You can, in theory, switch between these two instantly. Since you reset speedTimes only if there is a frame where the left and right are both pressed or both unpressed, if someone were to release right and hit left at such a time that the code below isn't executed, your xVel would invert itself entirely, causing the object to fly off in the other direction.

(A clever person could also abuse this to get arbitrarily high speeds, as speedTimes is incremented each time the trick is pulled off)


if(moveRight == false && moveLeft == false)
{
if(xVel >0)
{
speedTimes = 1;
xVel = -0.20;
}
if(xVel <0)
{
speedTimes = 1;
xVel = +0.20;
}
}
if(moveRight == true && moveLeft == true)
{
if(xVel >0)
{
speedTimes = 1;
xVel = -0.20;
}
if(xVel <0)
{
speedTimes = 1;
xVel = +0.20;
}
}


First off, you can simplify this. The bodies of the conditionals are the same, and you can test for both conditionals at once by seeing of moveRight and moveLeft are the same.

But, this doesn't seem to be what you want. If the person lets go of the keys, then xVel will be set to a constant - no matter how fast they were going, it'll be set. I suspect you want something more along the lines of xVel-=0.20;, as that will cause slowing down instead of instantly changing the speed to a constant.

Also, you set xVel to a negative value if it's positive, then have an if (xVel<0) clause right afterwards. That'll get triggered even if it's positive, since it will pass through the if(xVel>0) loop first, which will set it to a negative value. I suspect an else if clause was what was meant.

Though, even if you do all that, you'll still be vulnerable to the glitch in the other part of the code - pressing left or right will instantly set the velocity to that direction, regardless of your prior direction. In fact, every time you change xVel, you change it to a constant or a multiple of another variable (speedTimes) which is actually independant. I would not recommend that - set it based on its own value. It's easier and far less likely to end up with bugs like the directional switching one. I'd probably rewrite the thing like this:


if (moveRight == moveLeft)
{
if (xVel>0)
xVel-=1;
if (xVel<0)
xVel+=1;
}
else if (moveRight && xVel<maxSpeed)
{
xVel+=2;
}
else if (moveLeft && xVel>(maxSpeed * (-1)))
{
xVel-=2;
}


You could also use xVel as a float, and cast it as an int when using to to physically move the object. I just chose to make it an int here so I wouldn't have to deal with rounding errors (like it possibly never actually hitting zero and thus oscillating when moveRight==moveLeft).
When you want to do the same operation for multiple cases, you can use ||:


if( bottomA <= topB ||
topA >= bottomB ||
rightA <= leftB ||
leftA >= rightB ){
return false;
}

+---------------------------------------------------------------------+

| Game Dev video tutorials -> http://www.youtube.com/goranmilovano | +---------------------------------------------------------------------+

This topic is closed to new replies.

Advertisement