2D collision troubles

Started by
4 comments, last by valles 16 years, 5 months ago
I'm creating a 2D top-down RPG-type game, and the collision is giving me some troubles. My collision map is a list of rectangles, and I check the player character's collision box against this list of rectangles. It works rather well, with the player character sliding along the rectangles except when I hit a corner. Easier to explain with code: void CActor::Update( std::vector<CRect> &collisionBoxes ) { //Calculate velocity m_velocity = CVector2D( m_targetX - m_position.GetX(), m_targetY - m_position.GetY() ); m_velocity.Normalize(); m_velocity *= m_maxSpeed; //Move to new position m_position.SetX( m_position.GetX() + (int)m_velocity.GetX() ); m_position.SetY( m_position.GetY() + (int)m_velocity.GetY() ); //Cancel movement if there is a collision bool v = false; bool h = false; if( GetCollision().RectCollideV( collisionBoxes ) ) v = true; if( GetCollision().RectCollideH( collisionBoxes ) ) h = true; if (v) m_position.SetY( m_position.GetY() - (int)m_velocity.GetY() ); if (h) m_position.SetX( m_position.GetX() - (int)m_velocity.GetX() ); } The actor has a target and moves there in a straight line. If he encounters a vertical collision the Y part of his movement is canceled, and if he encounters a horizontal collision the X part of his movement is canceled. Naturally, when he hits the corner of a collision box both X and Y movement will be canceled causing him to freeze. How do I solve this problem?
Advertisement
you can try to find out which one he its first.

Everything is better with Metal.

But sometimes he hits a corner and therefore both sides at the same time. I tried making him remember which side(s) he hit in the previous frame, and that almost solved the problem. But adding another collision box adjacent to the first one, will enable the player to slide through one of them :(

See the code, and suggest me a new solution:

void CActor::Update( std::vector<CRect> &collisionBoxes ) {
//Calculate velocity
CVector2D velocityOld = m_velocity;
m_velocity = CVector2D( m_targetX - m_position.GetX(), m_targetY - m_position.GetY() );
m_velocity.Normalize();
m_velocity *= m_maxSpeed;

//Move to new position
m_position.SetX( m_position.GetX() + (int)m_velocity.GetX() );
m_position.SetY( m_position.GetY() + (int)m_velocity.GetY() );

//Cancel movement if there is a collision
bool v = GetCollision().RectCollideV( collisionBoxes );
bool h = GetCollision().RectCollideH( collisionBoxes );

if (v && !h) {
m_position.SetY( m_position.GetY() - (int)m_velocity.GetY() );
m_colV = true;
m_colH = false;
}
else if (h && !v) {
m_position.SetX( m_position.GetX() - (int)m_velocity.GetX() );
m_colH = true;
m_colV = false;
}
else if (h && v) {
if (m_colV && !m_colH) {
m_position.SetY( m_position.GetY() - (int)m_velocity.GetY() );
}
if (m_colH && !m_colV) {
m_position.SetX( m_position.GetX() - (int)m_velocity.GetX() );
}
if (!m_colV && !m_colH) {
m_position.SetY( m_position.GetY() - (int)m_velocity.GetY() );
m_position.SetX( m_position.GetX() - (int)m_velocity.GetX() );
}
}
else if (!h && !v) {
m_colV = false;
m_colH = false;
}

//Update the animation
HandleAnimation();
}
I solved it by doing an extra collision check after all the above. Would still like to hear about better solutions though :)
I had this exact same problem when I first started my side scroller :)

Someone here gave me some very helpful advice which was a different approach to the problem, and I am happy I can help someone else out who was in the same predicament! Here is what he told me to do (which worked perfectly). Try calculating the distance from the edges of the player to the edges of the rectangle. Or more specifically, the the top edge of the player needs the distance checked with the bottom edge of the rectangle, the bottom edge of the player needs the distance checked with the top edge of the rectangle, left edge of the player needs distance checked with the right edge of the rectangle, and the right edge of the player needs distance checked with the left edge of the rectangle.

One you have all four of these distances calculated, you will be able to see which one is the smallest. the smallest one is the side of the tile which you need to hold the player at. Give it a try, and if you are confused or run into problems, I'll be happy to further clarify!
I was having the same problem as you, and started learning www.box2d.org. There are three steps:
1. Store the edges of all collisions
2. Assign a corrective velocity for each collision edge
3. Update the system with the new velocities.

For boxes they are rotated to be AABB (Axis Aligned Bounding Boxes), then the edge with the most penetration is stored as the collision edge. Friction is calculated, so the collision edges are chopped to size.

If you find a simple solution I'd love to see it.

- valles

This topic is closed to new replies.

Advertisement