Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript canvas collision detection

I'm building a game in Javascript using canvas which requires collision detection, in this case if the player sprite hits a box, the player must not be allowed through the box.

I have a global array called blockList that holds all the boxes being drawn to the canvas. It looks like this:

var blockList = [[50, 400, 100, 100]];

And they're being drawn to the canvas like this:

c.fillRect(blockList[0][0], blockList[0][1], blockList[0][2], blockList[0][3]);

I also have a player object, which has an update method and a draw method. Update sets the position of the player based on keyboard input etc, and draw is used by the main game loop to draw the player to the canvas. The player is being drawn like this:

this.draw = function(timestamp) {
        if(this.state == "idle") {
            c.drawImage(this.idleSprite, this.idleSprite.frameWidth * this.idleSprite.frameCount, 0, this.idleSprite.frameWidth, this.idleSprite.frameHeight, this.xpos, this.ypos, this.idleSprite.frameWidth, this.idleSprite.frameHeight);
            if(timestamp - this.lastDraw > this.idleSprite.updateInterval) {
                this.lastDraw = timestamp;
                if(this.idleSprite.frameCount < this.idleSprite.frames - 1) { this.idleSprite.frameCount++; } else { this.idleSprite.frameCount = 0; }
            }
        } else if(this.state == "running") {
            var height = 0;
            if(this.facing == "left") { height = 37; }
            c.drawImage(this.runningSprite, this.runningSprite.frameWidth * this.runningSprite.frameCount, height, this.runningSprite.frameWidth, this.runningSprite.frameHeight, this.xpos, this.ypos, this.runningSprite.frameWidth, this.runningSprite.frameHeight);
            if(timestamp - this.lastDraw > this.runningSprite.updateInterval) {
                this.lastDraw = timestamp;
                if(this.runningSprite.frameCount < this.runningSprite.frames - 1) { this.runningSprite.frameCount++; } else { this.runningSprite.frameCount = 0; }
            }
        }
    }

Now, the player has certain properties being player.xpos, player.ypos, player.width, player.height. The same properties exist for the blocks. So I have everything I need to impliment collision detection, I just have no idea how to do it. I've tried doing things like:

if(player.x > blockList[0][0] && player.y > blockList[0][1])

but it's far from perfect or playable.

Does anyone know of a simple method or function to be able to return true or false based on if two objects are colliding?

like image 999
James Dawson Avatar asked Nov 05 '11 01:11

James Dawson


2 Answers

I use the following function for collision detection between two rectangles:

rect_collision = function(x1, y1, size1, x2, y2, size2) {
  var bottom1, bottom2, left1, left2, right1, right2, top1, top2;
  left1 = x1 - size1;
  right1 = x1 + size1;
  top1 = y1 - size1;
  bottom1 = y1 + size1;
  left2 = x2 - size2;
  right2 = x2 + size2;
  top2 = y2 - size2;
  bottom2 = y2 + size2;
  return !(left1 > right2 || left2 > right1 || top1 > bottom2 || top2 > bottom1);
};

This determines whether two squares, centered at (x1, y1) and (x2, y2), with side lengths 2*size1 and 2*size2, respectively, are overlapping. It should be easy enough to alter the definitions of left1, right1, etc. to deal with general rectangles rather than just squares and to use a different data format.

Specifically, left1 is the left side of the the first square, right1 the right side, etc. Note that, in my coordinate system, the y-axis is inverted (top1 < bottom1).

like image 154
Aaron Dufour Avatar answered Sep 19 '22 00:09

Aaron Dufour


You just want to know if two rectangles overlap?

Here is a bulletproof function for you:

// returns true if there is any overlap
// params: x,y,w,h of two rectangles
function intersects(x1, y1, w1, h1, x2, y2, w2, h2) {
  if (w2 !== Infinity && w1 !== Infinity) {
    w2 += x2;
    w1 += x1;
    if (isNaN(w1) || isNaN(w2) || x2 > w1 || x1 > w2) return false;
  }
  if (y2 !== Infinity && h1 !== Infinity) {
    h2 += y2;
    h1 += y1;
    if (isNaN(h1) || isNaN(y2) || y2 > h1 || y1 > h2) return false;
  }
  return true;
}

If your program can be certain that the numbers will always be finite you can use a simpler version:

// returns true if there is any overlap
// params: x,y,w,h of two rectangles
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;
}

What its doing is finding the where the right side and bottom side of the two rectangles are, then it sees if the second one starts outside of the first one or if the first one starts outside of the second one.

If either rectangle begins after the other one has ended, then there is no collision. Otherwise there must be a collision.

like image 30
Simon Sarris Avatar answered Sep 19 '22 00:09

Simon Sarris