John Santos
John Santos

Reputation: 51

Detect which side of a rectangle is colliding with another rectangle

i'm trying to develop a game using HTML5 canvas with javascript and I am having problems detecting collisions without hard coding it using the (x,y) coordinates of the two objects. In my code based from research, found out the common algorithm in checking if the two objects are colliding which is

Object1 = a moving player
Object2 = fixed (x,y) points. Non movable object

(object1.x < object2.x + object2.width  && object1.x + object1.width  > object2.x &&
    object1.y < object2.y + object2.height && object1.y + object1.height > object2.y)

I tried it and developed it with other codes and it works, it detects the collisions. But I have an issue with the collisions, it works with the condition in the left side but when the object1 goes to the top, bottom or right of the object2 it goes back to the object2's left side. In other words, if the object1 goes to the top/bottom or right it goes back to the left side. Any kind of help would be much appreciated.

function collide(object1, object2){

    //object1 player
    this.x = object1.x;
    this.y = object1.y;
    this.w = object1.w;
    this.h = object1.h;

    //object2 any object 
    this.x2 = object2.x;
    this.y2 = object2.y;
    this.w2 = object2.w;
    this.h2 = object2.h;

    //collisions 
    var isCorrect = false;
    var side = 0;

    if ((this.x < this.x2 + this.w2) && (this.x + this.w > this.x2) 
        && (this.y < this.y2 + this.h2) && (this.h + this.y > this.y2)){
        isCorrect = true;
    }

    if (this.x + this.w > this.x2){
        //left check
        side = 1;
    }else if (this.x < this.x2 + this.w2){
        //right check
        side = 2;
    }else if (this.y + this.h > this.y2){
        //bottom check
        side = 3;
    }else if (this.y < this.y2 + this.h2){
        //top check
        side = 4;
    }

    if (isCorrect){
        if ((this.x + this.w > this.x2) && side == 1){
            playerObj.x -= 5;
        }else if ((this.x < this.x2 + this.w2) && side == 2){
            playerObj.x += 5;
        }else if ((this.y + this.h > this.y2) && side == 3){
            playerObj.y += 5;
        }else if ((this.y < this.y2 + this.h2) && side == 4){
            playerObj.y -= 5;
        }
    }
}

Upvotes: 5

Views: 5914

Answers (1)

markE
markE

Reputation: 105035

This collision function will tell you which side of rect2 collided with rect1:

And of course, the opposite side of rect1 collided with rect2.

This function is a 2-pass test:

  • Test1: Check if the 2 rectangle's centers are close enough to be colliding

  • Test2: If yes, check the intersection depths to determine which side was most involved in the collision.

The collision function

function collide(r1,r2){
    var dx=(r1.x+r1.w/2)-(r2.x+r2.w/2);
    var dy=(r1.y+r1.h/2)-(r2.y+r2.h/2);
    var width=(r1.w+r2.w)/2;
    var height=(r1.h+r2.h)/2;
    var crossWidth=width*dy;
    var crossHeight=height*dx;
    var collision='none';
    //
    if(Math.abs(dx)<=width && Math.abs(dy)<=height){
        if(crossWidth>crossHeight){
            collision=(crossWidth>(-crossHeight))?'bottom':'left';
        }else{
            collision=(crossWidth>-(crossHeight))?'right':'top';
        }
    }
    return(collision);
}

Here's example code and a Demo:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
  var BB=canvas.getBoundingClientRect();
  offsetX=BB.left;
  offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }

var rects=[];
var rect1={ x:100,y:100,w:85,h:85,fill:'red'}
var rect2={ x:10,y:10,w:40,h:60,fill:'gold'}

$("#canvas").mousemove(function(e){handleMouseMove(e);});

draw();

function collide(r1,r2){
  var dx=(r1.x+r1.w/2)-(r2.x+r2.w/2);
  var dy=(r1.y+r1.h/2)-(r2.y+r2.h/2);
  var width=(r1.w+r2.w)/2;
  var height=(r1.h+r2.h)/2;
  var crossWidth=width*dy;
  var crossHeight=height*dx;
  var collision='none';
  //
  if(Math.abs(dx)<=width && Math.abs(dy)<=height){
    if(crossWidth>crossHeight){
      collision=(crossWidth>(-crossHeight))?'bottom':'left';
    }else{
      collision=(crossWidth>-(crossHeight))?'right':'top';
    }
  }
  return(collision);
}

function draw(){
  ctx.clearRect(0,0,cw,ch);
  ctx.fillStyle=rect1.fill;
  ctx.fillRect(rect1.x,rect1.y,rect1.w,rect1.h);
  ctx.fillStyle=rect2.fill;
  ctx.fillRect(rect2.x,rect2.y,rect2.w,rect2.h);
  var side=collide(rect1,rect2);
  ctx.fillStyle='green'
  if(side=='top'){ ctx.fillRect(rect2.x,rect2.y,rect2.w,3); }
  if(side=='right'){ ctx.fillRect(rect2.x+rect2.w,rect2.y,3,rect2.h); }
  if(side=='bottom'){ ctx.fillRect(rect2.x,rect2.y+rect2.h,rect2.w,3); }
  if(side=='left'){ ctx.fillRect(rect2.x,rect2.y,3,rect2.h); }
}

function handleMouseMove(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  rect2.x=mouseX;
  rect2.y=mouseY;

  draw();

}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Move the mouse to drag the gold rect<br>Any colliding side of gold rect will highlight</h4>
<canvas id="canvas" width=300 height=300></canvas>

Upvotes: 18

Related Questions