10
\$\begingroup\$

I'm having difficulties getting a moveable rectangle to collide with more than one rectangle.

I'm using SFML and it has a handy function called intersects which takes 2 rectangles and returns the intersections. I have a vector full of rectangles which I want my moveable rectangle to collide with. I'm looping through this using the following code (p is the moveble rectangle).

IsCollidingWith returns a bool but also uses SFML's intersects to work out the intersections.

while(unsigned i = 0; i!= testRects.size(); i++){
   if(p.IsCollidingWith(testRects[i]){
        p.Collide(testRects[i]);
   }
}

and the actual Collide() code:

void gameObj::collide( gameObj collidingObject ){

 printf("%f %f\n", this->colliderResult.width, this->colliderResult.height);

if (this->colliderResult.width < this->colliderResult.height) {
    // collided on X
    if (this->getCollider().left < collidingObject.getCollider().left ) {
        this->move( -this->colliderResult.width , 0);
    }else {
        this->move( this->colliderResult.width, 0 );
    }

}

if(this->colliderResult.width > this->colliderResult.height){
    if (this->getCollider().top < collidingObject.getCollider().top ) {
        this->move( 0, -this->colliderResult.height);
    }else {     
        this->move( 0, this->colliderResult.height );
    }

}

and the IsCollidingWith() code is:

bool gameObj::isCollidingWith( gameObj testObject ){
if (this->getCollider().intersects( testObject.getCollider(), this->colliderResult )) {
    return true;
}else {
    return false;
}

This works fine when there's only 1 Rect in the scene. However, when there's more than one Rect it causes an issue when working out 2 collisions at once.

Any idea how to deal with this correctly? I have uploaded a video to youtube to show my problem. The console on the far-right shows the width and height of the intersections. You can see on the console that it's trying to calculate 2 collisions at once, I think this is where the problem is being caused.

Finally, the image below seems to illustrate my problem nicely:

rectangle colliding with multiple other rectangles

\$\endgroup\$
3
  • \$\begingroup\$ The video link is broken. \$\endgroup\$ Mar 25, 2012 at 22:47
  • \$\begingroup\$ Are the collider objects returned by this->getCollider() updated by this->move() ?? \$\endgroup\$ Mar 25, 2012 at 23:34
  • \$\begingroup\$ Could you add some more information please? What exactly is the problem? The YouTube video seems to show predictable behavior, and there's only one other rectangle in the scene. \$\endgroup\$
    – Wackidev
    Jun 2, 2012 at 22:12

4 Answers 4

3
\$\begingroup\$

Your image illustrates one of the many problems with trying to use convex shapes - particularly rectangles - to simulate a flat surface like a floor. The algorithm leads to characters getting stuck on inner edges of the shapes making up the floor.

A simple fix for that is to check for vertical collision only, correct that, then check again for horizontal collision. Unless you're falling and trying to slide down a wall, in which case you want to check for horizontal collisions first. How do you know when to check which first? You could do it based on which component of your velocity is larger (if horizontal movement is greater, check vertical collisions first, otherwise check horizontal collisions).

What can be even simpler - and more performant - is to instead generate a list of edges for your world. That is, for your boxes making up the floor, set a flag indicating that only their top edge is actually collidable, and then ignore collisions with the other edges. You will not be able to use SFML's collision routine for that, but honestly simply box collisions is possibly the easiest bit of code you'll ever write in a game. This technique works particularly well if your world is aligned to a grid. I would check out the excellent Metanet tutorials (http://www.metanetsoftware.com/technique.html/) for that technique.

You're going to run into many others problems trying to build a simple 2D platformer game like you are. By far, the best resource I've seen latel for that is the following one, which you should read, and then read again:

http://higherorderfun.com/blog/2012/05/20/the-guide-to-implementing-2d-platformers/

\$\endgroup\$
1
\$\begingroup\$

Maybe a simple solution would be to check for collisions with every rectangle and move back, in the opposite direction of movement, until no collisions are detected. If that solves the problem, the implementation should be quite simple too.

\$\endgroup\$
1
0
\$\begingroup\$

I don't think having 2 collisions calculated is a problem, only a performance issue. In order for the collision to be handled correctly it may have to be tested twice. Using your diagram think if A is tested against B first, then it will need to be tested against the other boxes as well, and may well collide with another.

Hope that is helpful?

\$\endgroup\$
0
\$\begingroup\$

This isn’t really an optimal method, you should be trying to determine there earliest time when or the first point along the moving objects path where collisions occur and move the objects to that position (or the position at the calculated time), attempting to correct based on the penetrating positions after movement is very problematic, but you can use your current process with a small change. Just keep checking for collisions until there are none.

bool collided = true;
while(collided) {
   collided = false
   while(unsigned i = 0; i!= testRects.size(); i++){
      if(p.IsCollidingWith(testRects[i]){
         collided = true
         p.Collide(testRects[i]);
      }
   }
}

Note that there are situations where this will lead to an Infinite Loop (think of a situation where moving the box out of a collision results in another collection, and moving the box out of that collision moves it back into the same colliding position as before)

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .