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\$ Commented Jul 23, 2014 at 2:55

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.