Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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);
}
share|improve this question

1 Answer 1

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.

share|improve this answer
    
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. –  ndg Aug 27 '12 at 17:46
    
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". –  E-Man Aug 27 '12 at 18:04
    
@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. –  Jimmy Aug 27 '12 at 18:10
    
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. –  ndg Aug 27 '12 at 18:22
    
Apologies for not being clear in that comment: It would be better to step in one direction, resolve any collisions, and then repeat on the other direction. Determining which axis to step first is dependent on the game rules/state. –  E-Man Aug 27 '12 at 19:03

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.