1
\$\begingroup\$

I am making a simple game right now and am struggling with collision response. My goal is to someday be able to turn it into a 2D platformer but I have a long way to go. I am currently making this in JavaScript and using the canvas element so (0,0) is in the top left and positive X is to the right and positive Y is down.

I read a helpful post on StackExchange that got me started on this but I can't seem to get the algorithm 100% correct. How to deal with corner collisions in 2D?

I can detect the collision fine but I can't seem to get the response right. The goal is to detect which side the player hit first since minimum displacement doesn't always work. The X response seems to work fine but the Y only works when I am far from the corners. Here is a picture showing what happens Picture One showing the problem

Here is the code

var bx = box.x;
var by = box.y;
var bw = box.width;
var bh = box.height;

var boxCenterX = bx + (bw/2);
var boxCenterY = by + (bh/2);

var playerCenterX = player.x + player.xvel + (player.width/2);
var playerCenterY = player.y + player.yvel + (player.height/2);

//left = negative and right = positve, 0 = middle
var distanceXin = playerCenterX - boxCenterX;
var distanceYin = playerCenterY - boxCenterY;

var distanceWidth = Math.abs(distanceXin);
var distanceHeight = Math.abs(distanceYin);

var halfWidths = (bw/2) + (player.width/2);
var halfHeights = (bh/2) + (player.height/2);

if(distanceWidth < halfWidths){
    //xcollision
    if(distanceHeight < halfHeights){
            //ycollision                                                   
            if(player.xvel == 0){
                    //adjust y
                    if(distanceYin > 0){
                            //bottom
                            player.y = by + bh;
                            player.yvel = 0;
                    }else{
                            player.y = by - player.height;
                            player.yvel = 0;
                    }
            }else if(player.yvel == 0){
                    //adjust x
                    if(distanceXin > 0){
                            //right
                            player.x = bx + bw;
                            player.xvel = 0;
                    }else{
                            //left
                            player.x = bx - player.width;
                            player.xvel = 0;
                    }              
            }else{
                    var yTime = distanceYin / player.yvel;
                    var xTime = distanceXin / player.xvel;

                    if(xTime < yTime){
                            //adjust the x it collided first
                            if(distanceXin > 0){
                                    //right
                                    player.x = bx + bw;
                                    player.xvel = 0;
                            }else{
                                    //left
                                    player.x = bx - player.width;
                                    player.xvel = 0;
                            }
                    }else{
                            //adjust the y it collided first
                            if(distanceYin > 0){
                                    //bottom
                                    player.y = by + bh;
                                    player.yvel = 0;
                            }else{
                                    player.y = by - player.height;
                                    player.yvel = 0;
                            }
                    }                      
            }
      }
}

And here is a JSFiddle if you would like to see the problem yourself. http://jsfiddle.net/dMumU/

To recreate this move the player to here

Picture 2 Recreate in JSFiddle

And press up and left at the same time. The player will jump to the right for some reason.

Any advice? I know I am close but I can't seem to get xTime and yTime to equal what I want every time.

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

You are not using the right variable to perform the collision time calculation. distanceXin is the distance between the two objects’ centres, whereas what you actually want is the distance between the two objects’ edges.

Also, your code can be shortened and simplified a lot. Here is how I’d rewrite it:

var boxCenterX = box.x + (box.width / 2);
var boxCenterY = box.y + (box.height / 2);

var playerCenterX = player.x + player.xvel + (player.width / 2);
var playerCenterY = player.y + player.yvel + (player.height / 2);

// left = negative and right = positve, 0 = middle
var deltaCenterX = playerCenterX - boxCenterX;
var deltaCenterY = playerCenterY - boxCenterY;

// outside object = positive and inside object = negative
var deltaEdgeX = Math.abs(deltaCenterX) - (box.width + player.width) / 2;
var deltaEdgeY = Math.abs(deltaCenterY) - (box.height + player.height) / 2;

if (deltaEdgeX < 0 && deltaEdgeY < 0)
{
    if (deltaEdgeX * Math.abs(player.yvel) > deltaEdgeY * Math.abs(player.xvel))
    {
        // adjust the x it collided first
        player.x = box.x + (deltaCenterX > 0 ? box.width : -player.width);
        player.xvel = 0;
    }
    else
    {
        // adjust the y it collided first
        player.y = box.y + (deltaCenterY > 0 ? box.height : -player.height);
        player.yvel = 0;
    }
}

I’m afraid I must have done something wrong with your jsfiddle but this example should work: http://jsfiddle.net/4zh0aytd/

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

The error comes from yTime and xTime. Since you are using a rectangle that is wider than taller, your xTime is always greater than your yTime. This is way when colliding in the x directions it works, and when colliding in the y directions, the wrong thing happens. You can test this by swapping the width and height dimensions of the box, and the error will be just the opposite then.

You should be comparing how far the player is penetrating into the box's x space, with how far the player is penetrating into the box's y space; then based off whichever is great, adjust the player accordingly. I think this was your original intent.

Try replacing:

if(xTime < yTime){

with

if (halfWidths - distanceWidth < halfHeights - distanceHeight) {
\$\endgroup\$
1
  • \$\begingroup\$ Thanks for the help. Unfortunately that is not what I am trying to accomplish. By doing that I am responding to the minimum displacement which gives the incorrect response around corners especially when jumping. (My objects move kinda fast) That is why I was trying to work out the linear equation to find out what side hits first. I just can't figure out what exactly I need to be dividing to get my xTime and yTime variables. Maybe this question would be better for MathSE. \$\endgroup\$
    – WannaBe
    Jul 23, 2014 at 2:55

You must log in to answer this question.

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