-1

I'm trying to make a brick game with a ball and a player (platform). If the ball hits the player, it should go off in the other direction like ping pong. However, it's not detecting the collision.

Here's the code

html:

<canvas id="canvas" width= "400" height= "400"></canvas>

css:

#canvas{border:1px solid black}

Js:

var width = 400
var height = 400

var drawRect = function (x, y) {
  ctx.fillRect(x, y, 30, 5)
};

// The Ball constructor
var Player = function () {
  this.x = 395
  this.y = 395
  this.xSpeed = 5;
  this.ySpeed = 0;
};

// Update the ball's position based on its speed
Player.prototype.move = function () {
  this.x += this.xSpeed;
  this.y += this.ySpeed;

  if (this.x < 0) {
    this.x = width;
  } else if (this.x > width) {
    this.x = 0;
  } else if (this.y < 0) {
    this.y = height;
  } else if (this.y > height) {
    this.y = 0;
  }
};

// Draw the ball at its current position
Player.prototype.draw = function () {
  drawRect(this.x, this.y);
};

// Set the ball's direction based on a string
Player.prototype.setDirection = function (direction) {
    if (direction === "left") {
     this.xSpeed = -5;
     this.ySpeed = 0;
  } else if (direction === "right") {
     this.xSpeed = 5;
     this.ySpeed = 0;
  } else if (direction === "stop") {
     this.xSpeed = 0;
     this.ySpeed = 0;
  }
};

// Create the ball object
var player = new Player();

// An object to convert keycodes into action names
var keyActions = {
  32: "stop",
  37: "left",
  38: "up",
  39: "right",
  40: "down"
};

// The keydown handler that will be called for every keypress
$("html").keydown(function (event) {
  var direction = keyActions[event.keyCode];
  player.setDirection(direction);
});



var Ball = function () {
    this.x = 100;
    this.y = 100;
    this.xSpeed = -2
    this.ySpeed = 3;
};
var circle = function (x, y, radius, fillCircle) {
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, Math.PI * 2, false)
    if (fillCircle) {
        ctx.fill();
    } else {
        ctx.stroke();
    }
}
Ball.prototype.move = function () {
    this.x += this.xSpeed
    this.y += this.ySpeed
};

Ball.prototype.draw = function () {
    circle(this.x, this.y, 3, true);
};

Ball.prototype.checkCollision = function () {
    if (this.x < 0 || this.x > 400) {
        this.xSpeed = -this.xSpeed
    }

    if (this.y < 0) {
        this.ySpeed = -this.ySpeed

    }
};

Ball.prototype.checkCollisionPlayer = function () {
    if (this.x === Player.x || this.y === player.y) {
     this.ySpeed = -this.ySpeed
     this.xSpeed = -this.xSpeed
    }
}
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
setInterval(function () {
    ctx.clearRect(0, 0, 400, 400);
    player.draw();
    player.move();
    ball.draw();
    ball.move();
    ball.checkCollision();
    ball.checkCollisionPlayer();
}, 40);
var ball = new Ball();

Thanks for your support. :)

Victor Wei
  • 349
  • 1
  • 18
  • 2
    Why is the ball constructor called `Player()`? – Jonathan Lam Nov 10 '15 at 16:44
  • the player is the Rectangle and the ball is the ball – Victor Wei Nov 10 '15 at 16:44
  • anything wrong with that? – Victor Wei Nov 10 '15 at 16:45
  • 1
    It's fine if you know what you're doing, but I just feel like it's a bit counterintuitive. It seems like the Ball constructor should be `Ball()`. – Jonathan Lam Nov 10 '15 at 16:46
  • 2
    I voted to close because "Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers." – bhspencer Nov 10 '15 at 16:47
  • well im just trying to see how this is not working – Victor Wei Nov 10 '15 at 16:49
  • This condition looks wrong. `if (this.x === Player.x || this.y === player.y)` Dont you want to be checking if the ball is inside the player rectangle not that its position is equal to the edge of the player. Consider if the update of the ball position overshoots the player edge. – bhspencer Nov 10 '15 at 16:50
  • so how would i do that? – Victor Wei Nov 10 '15 at 16:51
  • You should re-post the question with a minimal example. e.g. how to write a single function that takes a point and a rectangle and returns true if the point is inside the rectangle. – bhspencer Nov 10 '15 at 16:52
  • 2
    The collision detection is only checking the X/Y coordinates. It doesn't account for the area of the shapes at all. The two objects would have to occupy the *exact same pixel* in order for this to detect a collision. – David Nov 10 '15 at 16:53
  • Or just look for other examples on SO. There are many e.g. http://stackoverflow.com/questions/15019176/javascript-function-to-determine-if-a-point-is-in-between-a-rectangle – bhspencer Nov 10 '15 at 16:53
  • 1
    `if (this.x === Player.x || this.y === player.y)` only check 1 single pixel of the entire ball and compares it to 1 single pixel of the player. Also, `Player` should be `player`. I could gave you an answer but you don't even show us that you try to understand what we are teaching you. – service-paradis Nov 10 '15 at 16:54
  • http://stackoverflow.com/questions/2440377/javascript-collision-detection – David Nov 10 '15 at 16:57
  • @SushiIsAwesome there are guidelines for asking questions on SO. – bhspencer Nov 10 '15 at 16:57
  • Create your case in https://jsfiddle.net/ – Valijon Nov 10 '15 at 16:58
  • 1
    Your problem is `it's not detecting the collision`, you ask for support (`Thanks for your support.` at the end of your post). Many comments here gave you that support by explaining why the collision is not detected and what you should try to make it works. Every comments here is helpful. But you don't even take time to read and understand what we're saying. You just wait here that someone make all the works for you. – service-paradis Nov 10 '15 at 17:08

1 Answers1

1

I've added a simple box collision and update the classes to have the box dimensions of the ball and player. Remember that the ball box has to adjust for radius offset.

update

The kind of collision detection needed for the boxes you have to know side or corner that was hit. I updated the example as a starting point for this but it's needs to have the corners added and I also don't know the optimal detection. The blocks are setup for testing. I hope this helps.

var width = 400
var height = 400

function Brick(x, y, w, h, color) {
  this.x = x;
  this.y = y;
  this.color = color;
  this.w = w;
  this.h = h;
  this.hits = 0;
}

Brick.prototype.draw = function() {
  ctx.save();
  ctx.fillStyle = this.color;
  ctx.fillRect(this.x, this.y, this.w, this.h);
  ctx.fillStyle = "white";
  ctx.fillText(this.hits, this.x + 2, this.y + 10);
  ctx.restore();
};

var bricks = [
  new Brick(80, 120, 15, 15, 'red'),
  new Brick(220, 90, 15, 15, 'blue'),
  new Brick(340, 100, 50, 20, 'green')
];




// The Ball constructor
var Player = function() {
  this.x = 395
  this.y = 395
  this.xSpeed = 5;
  this.ySpeed = 0;
  this.w = 30;
  this.h = 5;
};


// Update the ball's position based on its speed
Player.prototype.move = function() {
  this.x += this.xSpeed;
  this.y += this.ySpeed;

  if (this.x < 0) {
    this.x = width;
  } else if (this.x > width) {
    this.x = 0;
  } else if (this.y < 0) {
    this.y = height;
  } else if (this.y > height) {
    this.y = 0;
  }
};

// Draw the ball at its current position
Player.prototype.draw = function() {
  ctx.fillRect(this.x, this.y, this.w, this.h);
};

// Set the ball's direction based on a string
Player.prototype.setDirection = function(direction) {
  if (direction === "left") {
    this.xSpeed = -5;
    this.ySpeed = 0;
  } else if (direction === "right") {
    this.xSpeed = 5;
    this.ySpeed = 0;
  } else if (direction === "stop") {
    this.xSpeed = 0;
    this.ySpeed = 0;
  }
};

// Create the ball object
var player = new Player();

// An object to convert keycodes into action names
var keyActions = {
  32: "stop",
  37: "left",
  38: "up",
  39: "right",
  40: "down"
};

// The keydown handler that will be called for every keypress
$("html").keydown(function(event) {
  var direction = keyActions[event.keyCode];
  player.setDirection(direction);
});



var Ball = function() {
  this.x = 100;
  this.y = 100;
  this.xSpeed = -2
  this.ySpeed = 3;
  this.radius = 3;
};
var circle = function(x, y, radius, fillCircle) {
  ctx.beginPath();
  ctx.arc(x, y, radius, 0, Math.PI * 2, false)
  if (fillCircle) {
    ctx.fill();
  } else {
    ctx.stroke();
  }
}
Ball.prototype.move = function() {
  this.x += this.xSpeed
  this.y += this.ySpeed
  if ((this.y + this.radius) > ctx.canvas.height) {
    // floor to ceiling
    this.y = 0;
  }
};

Ball.prototype.draw = function() {
  circle(this.x, this.y, this.radius, true);
};

Ball.prototype.getBox = function() {
  return {
    x: this.x - this.radius,
    y: this.y - this.radius,
    w: this.radius * 2,
    h: this.radius * 2
  };
};

Ball.prototype.checkCollision = function() {




  if (this.x < 0 || this.x > 400) {
    this.xSpeed = -this.xSpeed
  }

  if (this.y < 0) {
    this.ySpeed = -this.ySpeed
  } else {
    var boxA = this.getBox();

    switch (boxCollide(boxA, player)) {
      case 1:
      case 3:
        this.ySpeed = -this.ySpeed;
        break;
      case 2:
      case 4:
        this.xSpeed = -this.xSpeed;
        break;
    }
  }


};

// does box a collide with box b
// box = {x:num,y:num,w:num,h:num}
function boxCollide(a, b) {

  var ax2 = a.x + a.w;
  var ay2 = a.y + a.h;
  var bx2 = b.x + b.w;
  var by2 = b.y + b.h;

  // simple hit true, false
  //if (ax2 < b.x || a.x > bx2 || ay2 < b.y || a.y > by2) return false;
  // return true

  var xInRange = (a.x >= b.x && a.x <= bx2 || ax2 >= b.x && ax2 <= bx2);
  var yInRange = (a.y >= b.y && a.y <= by2 || ay2 >= b.y && ay2 <= by2);
  // Clockwise hit from top 1,2,3,4 or -1
  if (ay2 > b.y && a.y < by2 && xInRange) return 1; // A hit the top of B
  if (a.x < bx2 && ax2 > b.x && yInRange) return 2; // A hit the right of B
  if (a.y < by2 && ay2 > b.y && xInRange) return 3; // A hit the bottom of B
  if (ax2 > b.x && a.x < bx2 && yInRange) return 4; // A hit the right of B
  return -1; // nohit
}


Ball.prototype.checkCollisionPlayer = function() {
  if (this.x === Player.x || this.y === player.y) {
    this.ySpeed = -this.ySpeed
    this.xSpeed = -this.xSpeed
  }
}
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
setInterval(function() {
    ctx.clearRect(0, 0, 400, 400);
    player.draw();
    player.move();
    ball.draw();
    ball.move();
    ball.checkCollision();
    ball.checkCollisionPlayer();
    var boxA = ball.getBox();
    for (var i = 0; i < bricks.length; i++) {
      switch (boxCollide(boxA, bricks[i])) {
        case 1:
          ball.y = bricks[i].y - ball.radius - 1;
          ball.ySpeed = -ball.ySpeed;
          bricks[i].hits++;
          break;
        case 3:
          ball.y = bricks[i].y + ball.radius + bricks[i].h + 1;
          ball.ySpeed = -ball.ySpeed;
          bricks[i].hits++;
          break;
        case 2:
          ball.x = bricks[i].x + ball.radius + bricks[i].w + 1;
          ball.xSpeed = -ball.xSpeed;
          bricks[i].hits++;
          break;
        case 4:
          ball.x = bricks[i].x - ball.radius - 1;
          ball.xSpeed = -ball.xSpeed;
          bricks[i].hits++;
          break;
      }


      bricks[i].draw();
    }
  },
  40);
var ball = new Ball();
#canvas {
  border: 1px solid black
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" width="400" height="400"></canvas>
wolfhammer
  • 2,641
  • 1
  • 12
  • 11
  • The collision detector tries to fail fast by returning false when the boxes can't possibly intersect. It should read like `if the right side of A doesn't reach the left side of B -OR- the left side of A is beyond the right side of B -OR- the bottom of A doesn't reach the top of B -OR- the top of A is beyond the bottom of B -THEN- the boxes don't instersect -ELSE- the boxes do interset`. It's a mouth full. Make some random coords and plot it out! – wolfhammer Nov 10 '15 at 19:09
  • also, if a created a brick, how would it work that way? I just want the collision effect answer. – Victor Wei Nov 10 '15 at 19:49
  • For "did ball hit brick?" you need to make the the box object for ball since x,y is center and not top left. Setup brick like player with x, y, w, h then you can check for a hit with `if (boxCollide(boxA, brick)) {...`. – wolfhammer Nov 10 '15 at 20:05
  • lol. I tried if boxCollide and ran into some problems. First issue is that the collider didn't know what side of the box was hit and you need that. Secondly when the object collides you need to move it out of the object it hits so it doesn't collide in the next frame. I've updated the example as a start. You may also need to test for the speed of the ball so that it doesn't pass through objects.. for instance if speedx is > the size of a block. – wolfhammer Nov 10 '15 at 21:09
  • hm... yeah seems like it doesn't detect the bottom – Victor Wei Nov 11 '15 at 01:12