3
\$\begingroup\$

I'm working on a simple collision detection and resolution method for a 2d tile-based bounding box system. Collision appears to work correctly, but I'm having issues with resolving a collision after it has happened.

Essentially what I'm attempting to do is very similar to this approach. The problem I'm experiencing is that because objects can be traveling with both horizontal and vertical velocity, my resolution code causes the object to jump incorrectly.

I've drawn the following annotation to explain my issue. In this example, because my object has both horizontal and vertical velocity, my object (which is heading upwards and collides with the bottom of a tile) has it's position altered twice:

  • To correctly adjust it's vertical position to be beneath the tile.
  • To incorrectly adjust it's horizontal position to be to the left of the tile.

Collision issue

Below is my collision/resolution code in full:

function intersects(x1, y1, w1, h1, x2, y2, w2, h2)
{
    w2 += x2;
    w1 += x1;
    if (x2 > w1 || x1 > w2) return false;
    h2 += y2;
    h1 += y1;
    if (y2 > h1 || y1 > h2) return false;
    return true;
}

for(var y = 0; y < this.game.level.tiles.length; y++)
{
    for(var x = 0; x < this.game.level.tiles[y].length; x++)
    {
        var tile = this.game.level.getTile(x, y);
        if(tile)
        {

            if(
                this.velocity.x > 0 &&
                intersects(this.position.x+dx+this.size.w, this.position.y+dy, 1, this.size.h, x*tileSize, y*tileSize, tileSize, tileSize)
            )
            {
                this.position.x = ((x*tileSize)-this.size.w);
                hitSomething = true;
                break;
            }
            else if(
                this.velocity.x < 0 &&
                intersects(this.position.x+dx, this.position.y+dy, 1, this.size.h, x*tileSize, y*tileSize, tileSize, tileSize)
            )
            {
                this.position.x = ((x*tileSize)+tileSize);
                hitSomething = true;
                break;
            }
            if(
                this.velocity.y > 0 &&
                intersects(this.position.x+dx, this.position.y+dy+this.size.h, this.size.w, 1, x*tileSize, y*tileSize, tileSize, tileSize)
            )
            {
                this.position.y = ((y*tileSize)-this.size.h);
                hitSomething = true;
                break;
            }
            else if(
                this.velocity.y < 0 &&
                intersects(this.position.x+dx, this.position.y+dy, this.size.w, 1, x*tileSize, y*tileSize, tileSize, tileSize)
            )
            {
                this.position.y = ((y*tileSize)+tileSize);
                hitSomething = true;
                break;
            }
        }
    }
}

if(hitSomething)
{
    this.velocity.x = this.velocity.y = 0;
    dx = dy = 0;

    this.setJumping(false);
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

There's a couple of issues with the posted code. Your collision-checking loop looks something like this:

for(Y) {
    for(X) { 
        if (collision) {
            hitSomething = true;
            fixPosition();
            break;
        }
    }
}

The break only breaks out of the X loop, it continues on the Y-loop. The block gets moved to the correct column, but it still intersects with a tile on the next row, so the block then gets moved again. So the first thing to do is to put the entire thing in a function, and replace the break with return.

Secondly, you always check X-intersection before Y-intersection. You have kind of "fixed" it by checking the leading edge of the moving object, but it still will detect the left/right edge before the top/bottom edge. I'd rather see an unbiased collision detection by checking intersects on the full box, and then based on the relative position of the two objects, deciding which way to fix the position and then bounce the velocity.

\$\endgroup\$
6
  • \$\begingroup\$ Thanks for pointing that out. A silly mistake on my part. Unfortunately the issue I'm struggling with is how best to handle the logic to determine how I go about 'fixing' the position after collision. \$\endgroup\$
    – ndg
    Commented Aug 27, 2012 at 17:46
  • \$\begingroup\$ Resolve the collision one-axis at a time: Detect-Resolve X-Detect again. You want to make sure you are resolving the correct axis first, so that you get your desired result (i.e. if your Y velocity > X velocity, resolve the Y axis first). That isn't really the best way to go about it, but it "works". \$\endgroup\$
    – Eric P.
    Commented Aug 27, 2012 at 18:04
  • \$\begingroup\$ @E-Man That is one way to solve the problem, though I'd go for a more correct approach than that. Biasing the check based on velocity is inaccurate when you "graze" the surface near a corner -- it would bounce off the wrong side of the tile. \$\endgroup\$
    – Jimmy
    Commented Aug 27, 2012 at 18:10
  • \$\begingroup\$ I'm not sure biasing the check would actually solve anything. An object could travel very quickly horizontally but ultimately collide against the underside of an object. \$\endgroup\$
    – ndg
    Commented Aug 27, 2012 at 18:22
  • 1
    \$\begingroup\$ @EricP. I'd recommend detecting all of your collisions, on all axes, before resolving. That's necessary if you're checking collisions between multiple moving objects. \$\endgroup\$
    – 3Dave
    Commented Apr 11, 2017 at 20:43

You must log in to answer this question.

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