so I made this way to detect and react to squares moving and touching other squares. It uses Pythagorean theorem to make a third parameter so you don't get two true if statements when squares touch. I recently have been attempting to use this method on a square to a rectangle, and cannot seem to get it working. I have drawn lines to help visualize what the code is doing. Anybody have any suggestions on how to get this collision working properly?
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let gravity = 1.5;
let friction = 0.9;
//CHARACTER:
class Player {
constructor(x, y, w, h, vx, vy, c, j) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.vx = vx;
this.vy = vy;
this.color = c;
this.jumping = j;
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {this.y = canvas.height - this.h; this.vy = 0; this.jumping = false};
}
update() {
this.draw();
this.vy += gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= friction;
this.vy *= friction;
this.canvasCollision() //must be after other updates
}
}
let player1 = new Player(75, canvas.height/2 + 75, 75, 75, 0, 0, '#8DAA9D', false);
function controlPlayer1(obj) {
//this order matters. If update is before jump then obj won't jump when on top of other block.
if (controller1.up1 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
if (controller1.left1) { obj.vx -= 0.5 };
if (controller1.right1) { obj.vx += 0.5 };
obj.update();
}
//MOVEMENT:
class Controller {
constructor() {
this.left1 = false;
this.up1 = false;
this.right1 = false;
this.down1 = false;
let controller1 = (e) => {
if (e.code === 'KeyD') { this.right1 = e.type === 'keydown' }
if (e.code === 'KeyA') { this.left1 = e.type === 'keydown' }
if (e.code === 'KeyW') { this.up1 = e.type === 'keydown' }
if (e.code === 'KeyS') { this.down1 = e.type === 'keydown' }
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
}
}
let controller1 = new Controller();
//PLATFORM
class Platform {
constructor(x, y, w, h, yv, c) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.yv = yv;
this.color = c;
}
draw(){
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
update(){
this.draw();
this.y += this.yv;
this.yv *= 0.9;// friction
}
}
let platform1 = new Platform(canvas.width/2, canvas.height - 150, 100, 30, 0, '#202020');
let platform2 = new Platform (canvas.width/4, canvas.height/2, 75, 75, 0, '#202020');
//COLLISION DETECTION
function platformDetection(obj, obj2){
//center point of each side of obj1
let objLeft = {x: obj.x, y: obj.y + obj.h/2};
let objTop = {x: obj.x + obj.w/2, y: obj.y};
let objRight = {x: obj.x + obj.w, y: obj.y + obj.h/2};
let objBottom = {x: obj.x + obj.w/2, y: obj.y + obj.h};
//center point of each side a obj2
let obj2Left = {x: obj2.x, y: obj2.y + obj2.h/2};
let obj2Top = {x: obj2.x + obj2.w/2, y: obj2.y};
let obj2Right = {x: obj2.x + obj2.w, y: obj2.y + obj2.h/2};
let obj2Bottom = {x: obj2.x + obj2.w/2, y: obj2.y + obj2.h};
//distance between obj1 and obj2 opposing sides
let rightDistX = objRight.x - obj2Left.x;
let rightDistY = objRight.y - obj2Left.y;
let leftDistX = objLeft.x - obj2Right.x;
let leftDistY = objLeft.y - obj2Right.y;
let topDistX = objTop.x - obj2Bottom.x;
let topDistY = objTop.y - obj2Bottom.y;
let bottomDistX = objBottom.x - obj2Top.x;
let bottomDistY = objBottom.y - obj2Top.y;
//pythagorean theorem for distance. dRight is from the right side of obj1 to the left of obj2. the rest follow suit.
let dRight = Math.sqrt(rightDistX*rightDistX + rightDistY*rightDistY);
let dLeft = Math.sqrt(leftDistX*leftDistX + leftDistY*leftDistY);
let dTop = Math.sqrt(topDistX*topDistX + topDistY*topDistY);
let dBottom = Math.sqrt(bottomDistX*bottomDistX + bottomDistY*bottomDistY);
//Math.min return the smallest value thus variable minimum will be which ever sides are closest together
let minimum = Math.min(dRight, dLeft, dBottom, dTop);
let val = 0;
//compare minimum to pythagorean theorem and set val based on which ever side is closest
if (dTop == minimum) {
val = 1;
//the context stuff can be deleted. It's just here for visual. The if statements can be one line each.
context.lineWidth = 2;
context.strokeStyle = 'blue';
context.beginPath();
context.moveTo(objTop.x, objTop.y);
context.lineTo(obj2Bottom.x, obj2Bottom.y);
context.stroke();
}
else if (dRight == minimum) {
val = 2;
context.strokeStyle = 'orange';
context.beginPath();
context.moveTo(objRight.x, objRight.y);
context.lineTo(obj2Left.x, obj2Left.y);
context.stroke();
}
else if (dBottom == minimum) {
val = 3;
context.strokeStyle = 'green';
context.beginPath();
context.moveTo(objBottom.x, objBottom.y);
context.lineTo(obj2Top.x, obj2Top.y);
context.stroke();
}
else if (dLeft == minimum) {
val = 4;
context.strokeStyle = 'pink';
context.beginPath();
context.moveTo(objLeft.x, objLeft.y);
context.lineTo(obj2Right.x, obj2Right.y);
context.stroke();
}
//pass the objects and val
platformAction(obj, obj2, val);
}
//ACTION
function platformAction(obj, obj2, val){
//player1 top to player2 bottom
if (obj.y <= obj2.y + obj2.h && obj2.y + obj2.h >= obj.y && val == 1) {
obj2.y = obj.y - obj2.h;
obj.y = obj2.y + obj2.h;
obj2.vy = 0;
obj2.jumping = false;
obj.jumping = true;
}
//player1 right to player2 left
if (obj.x + obj.w >= obj2.x && obj2.x <= obj.x + obj.w && val == 2) {
obj2.x = obj.x + obj.w;
obj.x = obj2.x - obj.w - 1;
obj2.vx = 0;
}
//player1 bottom to player2 top
if (obj.y + obj.h >= obj2.y && obj2.y <= obj.y + obj.h && val == 3) {
obj.y = obj2.y - obj.h;
obj2.y = obj.y + obj.h;
obj.vy = 0;
obj.jumping = false;
obj2.jumping = true;
}
//player1 left to player2 right
if (obj.x <= obj2.x + obj2.w && obj2.x + obj2.w >= obj.x && val == 4) {
obj2.x = obj.x - obj2.w;
obj.x = obj2.x + obj2.w + 1;
obj.vx = 0;
obj2.vx = 0;
}
}
function initObj(obj){
obj.update();
}
function loop() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
//PLATFORM
initObj(platform1);
initObj(platform2);
//PLATFORM DETECTION
platformDetection(player1, platform1);
platformDetection(player1, platform2);
//PLAYER
controlPlayer1(player1);
requestAnimationFrame(loop)
}
loop();
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi">
<title>Tone.io</title>
<style>
body {
height:100vh;
width:100vh;
margin: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src = "Js/Tone.js" type = "text/javascript"></script>
</body>
</html>
#MPdoor2 "I made...". Anywho, when I gave you that code I did specifically say I had hacked it from a method I created to be used on tilemaps. The method works flawless for that purpose although there is more to the code and yes it is built for squares since that's what tiles map mainly are.
I have been playing with alternate methods of doing CD. Here's a shorter method that (so far) seems to be working well. This method still determines the distance between each side but in a different way. Once the broadphase determines a collision has occurred it calls the narrow phase and whichever side has the shortest distance is the side being penetrated. i.e. when you collide with another block from the player right to the object left we know that even the Y axis penetrates (top and bottom corners of player). This calculates the distance between all three and since the distance between X would be 0 it is the shortest and the CD for moving the player in the Y direction does not get called.
Try the snippet below and see if this works for you.
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
canvas.width = 700;
canvas.height = 500;
let gravity = 1.5;
let friction = 0.9;
//CHARACTER:
class Player {
constructor(x, y, vx, vy, c, j) {
//each player must have separate values for control purposes i.e. velocity/jump/attack etc.
this.x = x;
this.y = y;
this.w = 50;
this.h = 50;
this.vx = vx;
this.vy = vy;
this.color = c;
this.jumping = j;
this.center = {x: this.x + this.w/2, y: this.y + this.h/2};
this.topLeft = {x: this.x, y: this.y};
this.topRight = {x: this.x + this.w, y: this.y};
this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
this.bottomLeft = {x: this.x, y: this.y + this.h};
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {this.y = canvas.height - this.h; this.vy = 0; this.jumping = false};
}
update() {
this.draw();
this.vy += gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= friction;
this.vy *= friction;
this.center = {x: this.x + this.w/2, y: this.y + this.h/2};
this.topLeft = {x: this.x, y: this.y};
this.topRight = {x: this.x + this.w, y: this.y};
this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
this.bottomLeft = {x: this.x, y: this.y + this.h};
this.canvasCollision() //must be after other updates
}
}
let player = new Player(0, 0, 0, 0, 'red', false);
function controlPlayer1(obj) {
if (controller1.up1 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
if (controller1.left1) { obj.vx -= 0.5 };
if (controller1.right1) { obj.vx += 0.5 };
obj.update();
}
class Platform {
constructor(x,y,w,h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.center = {x: this.x + this.w/2, y: this.y + this.h/2};
this.topLeft = {x: this.x, y: this.y};
this.topRight = {x: this.x + this.w, y: this.y};
this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
this.bottomLeft = {x: this.x, y: this.y + this.h};
}
draw() {
context.fillStyle = 'blue';
context.fillRect(this.x, this.y, this.w, this.h);
}
}
let platforms = [];
function createPlatforms() {
for (let i=0; i<4; i++) {
platforms.push(new Platform(200 * i, 450 - (i * 75), 100, 25));
}
}
createPlatforms();
let platform1 = platforms.push(new Platform(300, 400, 50, 100));
let platform2 = platforms.push(new Platform(400, 400, 50, 50));
//MOVEMENT:
class Controller {
constructor() {
this.left1 = false;
this.up1 = false;
this.right1 = false;
this.left2 = false;
this.up2 = false;
this.right2 = false;
let controller1 = (e) => {
if (e.code === 'ArrowRight') { this.right1 = e.type === 'keydown' }
if (e.code === 'ArrowLeft') { this.left1 = e.type === 'keydown' }
if (e.code === 'ArrowUp') { this.up1 = e.type === 'keydown' }
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
}
}
let controller1 = new Controller();
function collisionDetection(obj) {
if (player.x + player.w < obj.x ||
player.x > obj.x + obj.w ||
player.y + player.h < obj.y ||
player.y > obj.y + obj.h) {
return
}
narrowPhase(obj);
}
function narrowPhase(obj) {
let playerTop_ObjBottom = Math.abs(player.y - (obj.y + obj.h));
let playerRight_ObjLeft = Math.abs((player.x + player.w) - obj.x);
let playerLeft_ObjRight = Math.abs(player.x - (obj.x + obj.w));
let playerBottom_ObjTop = Math.abs((player.y + player.h) - obj.y);
if ((player.y <= obj.y + obj.h && player.y + player.h > obj.y + obj.h) && (playerTop_ObjBottom < playerRight_ObjLeft && playerTop_ObjBottom < playerLeft_ObjRight)) {
player.y = obj.y + obj.h;
player.vy = 0;
}
if ((player.y + player.h >= obj.y && player.y < obj.y) && (playerBottom_ObjTop < playerRight_ObjLeft && playerBottom_ObjTop < playerLeft_ObjRight)) {
player.y = obj.y - player.h;
player.jumping = false;
player.vy = 0;
}
if ((player.x + player.w >= obj.x && player.x < obj.x) && (playerRight_ObjLeft < playerTop_ObjBottom && playerRight_ObjLeft < playerBottom_ObjTop)) {
player.x = obj.x - player.w;
player.vx = 0;
}
if ((player.x <= obj.x + obj.w && player.x + player.w > obj.x + obj.w) && (playerLeft_ObjRight < playerTop_ObjBottom && playerLeft_ObjRight < playerBottom_ObjTop)) {
player.x = obj.x + obj.w;
player.vx = 0;
}
}
function animate() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
controlPlayer1(player);
for (let i=0;i<platforms.length;i++) {
platforms[i].draw();
collisionDetection(platforms[i])
};
requestAnimationFrame(animate)
}
animate();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height">
<title>CD</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="squareRectCollision2.js"></script>
</body>
</html>
To detect collisions between rectangles, use the position of their sides. For instance, they "collide vertically" if:
object1's bottom side is below object2's bottom, and object1's top is above object2's bottom
or object1's top side is above object2's top, and object1's bottom is below object2's top
Consider they do collide if they collide both "vertically" and "horizontally".
Now once you've determined that, you need to detect from which edge. I'm using the closest one here, evaluating the distance for all that "collide vertically" or "horizontally".
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
canvas.width = Math.min(window.innerWidth - 3, 800);
canvas.height = Math.min(window.innerHeight - 3, 200);
let gravity = 1.5;
let friction = 0.9;
//CHARACTER:
class Player {
constructor(x, y, w, h, vx, vy, c, j) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.vx = vx;
this.vy = vy;
this.color = c;
this.jumping = j;
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {
this.y = canvas.height - this.h;
this.vy = 0;
this.jumping = false
};
}
update() {
this.draw();
this.vy += gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= friction;
this.vy *= friction;
this.canvasCollision() //must be after other updates
}
}
let player1 = new Player(75, canvas.height / 2 + 75, 75, 75, 0, 0, '#8DAA9D', false);
function controlPlayer1(obj) {
//this order matters. If update is before jump then obj won't jump when on top of other block.
if (controller1.up1 && !obj.jumping) {
obj.vy -= 25;
obj.jumping = true
};
if (controller1.left1) {
obj.vx -= 0.5
};
if (controller1.right1) {
obj.vx += 0.5
};
obj.update();
}
//MOVEMENT:
class Controller {
constructor() {
this.left1 = false;
this.up1 = false;
this.right1 = false;
this.down1 = false;
let controller1 = (e) => {
if (e.code === 'KeyD') {
this.right1 = e.type === 'keydown'
}
if (e.code === 'KeyA') {
this.left1 = e.type === 'keydown'
}
if (e.code === 'KeyW') {
this.up1 = e.type === 'keydown'
}
if (e.code === 'KeyS') {
this.down1 = e.type === 'keydown'
}
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
}
}
let controller1 = new Controller();
//PLATFORM
class Platform {
constructor(x, y, w, h, yv, c) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.yv = yv;
this.color = c;
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
update() {
this.draw();
this.y += this.yv;
this.yv *= 0.9; // friction
}
}
let platform1 = new Platform(canvas.width / 2, canvas.height - 150, 100, 30, 0, '#202020');
let platform2 = new Platform(canvas.width / 4, canvas.height / 2, 75, 75, 0, '#202020');
//COLLISION DETECTION
function platformDetection(obj, obj2) {
let ol1 = obj.x;
let ot1 = obj.y;
let or1 = ol1 + obj.w;
let ob1 = ot1 + obj.h;
let ol2 = obj2.x;
let ot2 = obj2.y;
let or2 = ol2 + obj2.w;
let ob2 = ot2 + obj2.h;
let [collideTop, collideBottom, collideRight, collideLeft] = [-1, -1, -1, -1];
if (ob1 >= ob2) {
if (ot1 <= ob2) {
collideTop = ob2 - ot1;
}
}
if (ol1 <= ol2) {
if (or1 >= ol2) {
collideRight = or1 - ol2;
}
}
if (ot1 <= ot2) {
if (ob1 >= ot2) {
collideBottom = ob1 - ot2;
}
}
if (or1 >= or2) {
if (ol1 <= or2) {
collideLeft = or2 - ol1;
}
}
let min = minOfVals(collideTop, collideRight, collideBottom, collideLeft);
let val = 0;
if (min >= 0) {
if (min == collideTop && (collideLeft >= 0 || collideRight >= 0)) {
val = 1;
} else if (min == collideRight && (collideTop >= 0 || collideBottom >= 0)) {
val = 2;
} else if (min == collideBottom && (collideLeft >= 0 || collideRight >= 0)) {
val = 3;
} else if (min == collideLeft && (collideTop >= 0 || collideBottom >= 0)) {
val = 4;
}
}
if (val) console.log(val, min, collideTop, collideRight, collideBottom, collideLeft);
//pass the objects and val
platformAction(obj, obj2, val);
}
function minOfVals(...vals) {
let min = -1;
function isBelowMin(v) {
return v >= 0 && (min < 0 || v < min);
}
for (v of vals) {
if (isBelowMin(v)) min = v;
}
return min;
}
//ACTION
function platformAction(obj, obj2, val) {
//player1 top to player2 bottom
if (val == 1) {
console.log("colliding from top");
obj2.y = obj.y - obj2.h;
//obj.y = obj2.y + obj2.h; //useless
obj2.vy = 0;
obj2.jumping = false;
obj.jumping = true;
}
//player1 right to player2 left
if (val == 2) {
console.log("colliding from right");
obj2.x = obj.x + obj.w;
obj.x = obj2.x - obj.w - 1;
obj2.vx = 0;
}
//player1 bottom to player2 top
if (val == 3) {
console.log("colliding from bottom");
obj.y = obj2.y - obj.h;
//obj2.y = obj.y + obj.h; //useless
obj.vy = 0;
obj.jumping = false;
obj2.jumping = true;
}
//player1 left to player2 right
if (val == 4) {
console.log("colliding from left");
obj2.x = obj.x - obj2.w;
obj.x = obj2.x + obj2.w + 1;
obj.vx = 0;
obj2.vx = 0;
}
}
function initObj(obj) {
obj.update();
}
function loop() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
//PLATFORM
initObj(platform1);
initObj(platform2);
//PLATFORM DETECTION
platformDetection(player1, platform1);
platformDetection(player1, platform2);
//PLAYER
controlPlayer1(player1);
requestAnimationFrame(loop)
}
loop();
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi">
<title>Tone.io</title>
<style>
html {
margin: 0;
padding: 0;
}
body {
height: 100vh;
width: 100vh;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="Js/Tone.js" type="text/javascript"></script>
</body>
</html>
There's still a different behavior when your controlled player comes from the left or from the right: push speed is not the same. But that's not due to collisions, so I'm letting you figure out this one (maybe it's on purpose too).
Yo, thx Justin for a solution. I took that solution and also added it to a previous question I asked, which was how to do the collision detection and action for two squares that have movement (with arrows and wasd). This is how I added it:
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let gravity = 1.5;
let friction = 0.9;
//CHARACTER:
class Player {
constructor(x, y, w, h, vx, vy, c, j) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.vx = vx;
this.vy = vy;
this.color = c;
this.jumping = j;
this.center = {x: this.x + this.w/2, y: this.y + this.h/2};
this.topLeft = {x: this.x, y: this.y};
this.topRight = {x: this.x + this.w, y: this.y};
this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
this.bottomLeft = {x: this.x, y: this.y + this.h};
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {this.y = canvas.height - this.h; this.vy = 0; this.jumping = false};
}
update() {
this.draw();
this.vy += gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= friction;
this.vy *= friction;
this.canvasCollision() //must be after other updates
}
}
let player1 = new Player(canvas.width/2, canvas.height/2 + 75, 75, 75, 0, 0, 'darkgrey', false);
let player2 = new Player(75, canvas.height/2 + 75, 75, 75, 0, 0, '#8DAA9D', false);
function controlPlayer1(obj) {
//this order matters. If update is before jump then obj won't jump when on top of other block.
if (controller1.up1 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
if (controller1.left1) { obj.vx -= 0.5 };
if (controller1.right1) { obj.vx += 0.5 };
obj.update();
}
function controlPlayer2(obj) {
if (controller2.up2 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
if (controller2.right2) { obj.vx += 0.5 };
if (controller2.left2) { obj.vx -= 0.5 };
obj.update();
}
//MOVEMENT:
class Controller {
constructor() {
this.left1 = false;
this.up1 = false;
this.right1 = false;
this.left2 = false;
this.up2 = false;
this.right2 = false;
this.down = false;
let controller1 = (e) => {
if (e.code === 'ArrowRight') { this.right1 = e.type === 'keydown' }
if (e.code === 'ArrowLeft') { this.left1 = e.type === 'keydown' }
if (e.code === 'ArrowUp') { this.up1 = e.type === 'keydown' }
if (e.code === 'ArrowDown') { this.down = e.type === 'keydown' }
}
let controller2 = (e) => {
if (e.code === 'KeyD') { this.right2 = e.type === 'keydown' }
if (e.code === 'KeyA') { this.left2 = e.type === 'keydown' }
if (e.code === 'KeyW') { this.up2 = e.type === 'keydown' }
if (e.code === 'KeyS') { this.down = e.type === 'keydown' }
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
window.addEventListener('keydown', controller2);
window.addEventListener('keyup', controller2);
}
}
let controller1 = new Controller();
let controller2 = new Controller();
//COLLISION DETECTION
function collisionDetection(obj, obj2){
if (obj.x + obj.w <= obj2.x ||
obj.x >= obj2.x + obj2.w ||
obj.y + obj.h < obj2.y ||
obj.y > obj2.y + obj2.h)
return
collisionAction(obj, obj2);
}
//ACTION
function collisionAction(obj, obj2){
let playerTop_ObjBottom = Math.abs(obj.y - (obj2.y + obj2.h));
let playerRight_ObjLeft = Math.abs((obj.x + obj.w) - obj2.x);
let playerLeft_ObjRight = Math.abs(obj.x - (obj2.x + obj2.w));
let playerBottom_ObjTop = Math.abs((obj.y + obj.h) - obj2.y);
//virtical obj 1 top
if ((obj.y <= obj2.y + obj2.h && obj.y + obj.h > obj2.y + obj2.h) && (playerTop_ObjBottom < playerRight_ObjLeft && playerTop_ObjBottom < playerLeft_ObjRight)) {
obj2.y = obj.y - obj2.h;
obj2.jumping = false;
obj2.vy = 0;}
//virtical obj 1 bottom
if ((obj.y + obj.h >= obj2.y && obj.y < obj2.y) && (playerBottom_ObjTop < playerRight_ObjLeft && playerBottom_ObjTop < playerLeft_ObjRight)) {
obj.y = obj2.y - obj.h;
obj.jumping = false;
obj.vy = 0;}
//horizontal obj 1 right
if ((obj.x + obj.w >= obj2.x && obj.x < obj2.x) && (playerRight_ObjLeft < playerTop_ObjBottom && playerRight_ObjLeft < playerBottom_ObjTop)) {
obj2.x = obj.x + obj.w;
obj.x = obj2.x - obj.w;
obj.vx = 0;
obj2.vx = 0;}
//horizontal obj1 left
if ((obj.x <= obj2.x + obj2.w && obj.x + obj.w > obj2.x + obj2.w) && (playerLeft_ObjRight < playerTop_ObjBottom && playerLeft_ObjRight < playerBottom_ObjTop)) {
obj2.x = obj.x - obj2.w;
obj.x = obj2.x + obj2.w;
obj.vx = 0;
obj2.vx = 0;}
}
function loop() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
//PLAYER
controlPlayer1(player1);
controlPlayer2(player2);
collisionDetection(player1, player2)
requestAnimationFrame(loop)
}
loop();
Related
I'm using Javascript to make a game and my character is supposed to move a box only when it's touching the box, but it always moves and when my character is on the same y-axis as the right side of of the box the game freezes, even if my character is not touching the right side of the box. I only coded the box to be able to be pushed left so far. I want to make sure that it can move left properly before I move on to making it be able to move elsewhere. My character does not move, instead everything but the character moves to make it seem like its moving without having to have different areas and stuff. There is code for other things, but its not used so I can isolate the code for the box. My code is mostly Javascript but does have a little bit of html.
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
canvas {
border:1px solid #d3d3d3;
background-color: #f1f1f1;
}
</style>
</head>
<body onload="startGame()">
<script src="script2.js"></script>
</body>
</html>
var wall
var myGamePiece;
var myObstacle;
var object1;
var objects;
var box;
function startGame() {
//creation of objects
wall = new component(30, 100, "black", 300, 200);
myGamePiece = new component(30, 30, "red", 240, 135);
myObstacle = new component(100, 100, "green", 200, 100);
myMap = new component(0, 0, "map.jpeg", -100, -100, "image")
object1 = new component(30, 30, "gray", 340, 125);
box = new component(30, 30, "blue", 290, 145);
myGameArea.start();
}
var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
myGameArea.key = e.keyCode;
})
window.addEventListener('keyup', function (e) {
myGameArea.key = false;
})
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
collide : function() {
object1.x += 1;
object1.y += 1;
},
boxup : function() {
box.x -= 1;
}
}
function component(width, height, color, x, y, type) {
this.type = type;
if (type == "image") {
this.image = new Image();
this.image.src = color;
}
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
if (type == "image") {
ctx.drawImage(this.image,
this.x,
this.y,
this.width,
this.height);
} else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
this.crashWith = function(object1) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = object1.x;
var otherright = object1.x + (object1.width);
var othertop = object1.y;
var otherbottom = object1.y + (object1.height);
var crash = true;
if ((mybottom < othertop) ||
(mytop > otherbottom) ||
(myright < otherleft) ||
(myleft > otherright)) {
crash = false;
}
return crash;
}
this.pushboxleftWith = function(box) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var boxleft = box.x;
var boxright = box.x + (box.width);
var boxtop = box.y;
var boxbottom = box.y + (box.height);
var pushboxdown = true;
var pushboxup = true;
var pushboxleft = true;
var pushboxright = true;
if (mybottom < boxtop) {
pushboxdown = true;
}
if (mytop > boxbottom) {
pushboxup = true;
}
if (myright < boxleft) {
pushboxright = true;
}
if ((myleft > boxright) && (((myBottom <= boxtop) && (myBottom >= boxbottom)) || ((myTop <= boxtop) && (myTop >= boxbottom)))) {
pushboxleft = false;
}
return pushboxleft;
return pushboxright;
return pushboxdown;
return pushboxup;
}
}
function updateGameArea() {
if (myGamePiece.crashWith(object1)) {
myGameArea.collide();
} else {
if (myGamePiece.pushboxleftWith(box)) {
myGameArea.boxup();
}
myGameArea.clear();
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
//keyboard controls. work but look ugly. moves everything but the player
if (myGameArea.key && myGameArea.key == 37) {myObstacle.x += 2; myMap.x += 2;object1.x+=2; box.x += 2; wall.x += 2;}
if (myGameArea.key && myGameArea.key == 39) {myObstacle.x += -2; myMap.x += -2; object1.x+=-2; box.x += -2; wall.x += -2;}
if (myGameArea.key && myGameArea.key == 38) {myObstacle.y += 2; myMap.y += 2; object1.y+=2; box.y += 2; wall.y += 2}
if (myGameArea.key && myGameArea.key == 40) {myObstacle.y+=-2; object1.y += -2; myMap.y += -2; box.y += -2; wall.y += -2}
//other square movement. disabled to isolate code.
/*
if (object1.x < myGamePiece.x) {
object1.x += 1;
}
if (object1.x > myGamePiece.x) {
object1.x += -1;
}
if (object1.y < myGamePiece.y) {
object1.y += 1;
}
if (object1.y > myGamePiece.y) {
object1.y += -1;
}
*/
/* object order: the object that is higher on the list
will be on top of objects lower on the list
*/
myMap.update();
myObstacle.update();
myGamePiece.newPos();
myGamePiece.update();
wall.update();
object1.update();
box.update();
//end of list
}
}```
My game piece won't move properly and freezes the game
As Baro pointed out, JavaScript variables are case-sensitive, meaning mybottom and myBottom are completely different variables. So when you tried to access myBottom, having only set mybottom, you got an error. Your game froze because you were running that code in your set interval, every 20 milliseconds.
Here is a working version of your code that you can play around with. I have refactored it a little:
let objects;
let wall = new component(30, 100, "black", 300, 200);
let myGamePiece = new component(30, 30, "red", 240, 135);
let myObstacle = new component(100, 100, "green", 200, 100);
let myMap = new component(0, 0, "orange", -100, -100)
let object1 = new component(30, 30, "gray", 340, 125);
let box = new component(30, 30, "blue", 290, 145);
const myGameArea = {
canvas: document.querySelector("#game-area"),
start: function() {
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function(e) {
myGameArea.key = e.keyCode;
})
window.addEventListener('keyup', function(e) {
myGameArea.key = false;
})
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
collide: function() {
object1.x += 1;
object1.y += 1;
},
boxup: function() {
box.x -= 1;
}
}
myGameArea.start();
function component(width, height, color, x, y) {
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
this.crashWith = function(object1) {
let myleft = this.x;
let myright = this.x + (this.width);
let mytop = this.y;
let mybottom = this.y + (this.height);
let otherleft = object1.x;
let otherright = object1.x + (object1.width);
let othertop = object1.y;
let otherbottom = object1.y + (object1.height);
let crash = true;
if ((mybottom < othertop) ||
(mytop > otherbottom) ||
(myright < otherleft) ||
(myleft > otherright)) {
crash = false;
}
return crash;
}
this.pushboxleftWith = function(box) {
let myleft = this.x;
let myright = this.x + (this.width);
let mytop = this.y;
let mybottom = this.y + (this.height);
let boxleft = box.x;
let boxright = box.x + (box.width);
let boxtop = box.y;
let boxbottom = box.y + (box.height);
let pushboxdown = true;
let pushboxup = true;
let pushboxleft = true;
let pushboxright = true;
if (mybottom < boxtop) pushboxdown = true;
if (mytop > boxbottom) pushboxup = true;
if (myright < boxleft) pushboxright = true;
if ((myleft > boxright) && (((mybottom <= boxtop) && (mybottom >= boxbottom)) || ((mytop <= boxtop) && (mytop >= boxbottom)))) {
pushboxleft = false;
}
return pushboxleft;
return pushboxright;
return pushboxdown;
return pushboxup;
}
}
function updateGameArea() {
if (myGamePiece.crashWith(object1)) {
myGameArea.collide();
} else {
if (myGamePiece.pushboxleftWith(box)) {
myGameArea.boxup();
}
myGameArea.clear();
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
//keyboard controls. moves everything but the player
if (myGameArea.key && myGameArea.key == 37) {
myObstacle.x += 2;
myMap.x += 2;
object1.x += 2;
box.x += 2;
wall.x += 2;
}
if (myGameArea.key && myGameArea.key == 39) {
myObstacle.x += -2;
myMap.x += -2;
object1.x += -2;
box.x += -2;
wall.x += -2;
}
if (myGameArea.key && myGameArea.key == 38) {
myObstacle.y += 2;
myMap.y += 2;
object1.y += 2;
box.y += 2;
wall.y += 2
}
if (myGameArea.key && myGameArea.key == 40) {
myObstacle.y += -2;
object1.y += -2;
myMap.y += -2;
box.y += -2;
wall.y += -2
}
myMap.update();
myObstacle.update();
myGamePiece.newPos();
myGamePiece.update();
wall.update();
object1.update();
box.update();
}
}
canvas {
border: 1px solid #d3d3d3;
background-color: #f1f1f1;
}
<body>
<script src="script2.js" type="module"></script>
<canvas id="game-area" width="480" height="270"></canvas>
</body>
I have run into a problem with my program, which essentially moves 100 figures on a browser window and changes their direction if the user presses a key down. In order to determine if a user has pressed a key, I used an event listener, but the problem is that the event listener only works for two of the figures on the screen. I can only change the direction of two figures, while the other figures continue to move around the screen and not respond to user input. I have attached all of my code below, and I would really appreciate if someone could help me figure out the problem! Thanks!
let canvas = document.querySelector("canvas")
canvas.width = window.innerWidth
canvas.height = window.innerHeight
// console.log(innerWidth)
// console.log(innerHeight)
let c = canvas.getContext("2d")
let changeDirection = undefined
let repeatone = 0
let repeattwo = 0
window.addEventListener('keydown', function(event) {
repeatone = 0
repeattwo = 0
changeDirection = true
console.log(changeDirection)
})
window.addEventListener('keyup', function(event) {
changeDirection = false
console.log(changeDirection);
})
function random_rgba() {
var o = Math.round,
r = Math.random,
s = 255;
return 'rgba(' + o(r() * s) + ',' + o(r() * s) + ',' + o(r() * s) + ',' + r().toFixed(1) + ')';
}
// c.arc(xCircle, yCircle, radius, 0, Math.PI * 2, false)
function Circle(x, y, dx, dy, radius, color) {
this.x = x
this.y = y
this.dx = dx
this.dy = dy
this.radius = radius
this.color = color
this.draw = function() {
// c.strokeStyle = this.color
c.beginPath()
c.strokeStyle = this.color
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false)
c.stroke()
}
this.update = function() {
repeatone += 1
if (changeDirection === true && repeatone <= 1) {
this.dx = -this.dx
this.dy = -this.dy
if (this.x >= innerWidth - this.radius) {
this.dx = -this.dx
} else if (this.x <= 0) {
this.dx = -this.dx
}
if (this.y >= innerHeight - this.radius) {
this.dy = -this.dy
} else if (this.y <= 0) {
this.dy = -this.dy
}
this.x += this.dx
this.y += this.dy
this.draw()
} else if (changeDirection === false || repeatone > 1) {
if (this.x >= innerWidth - this.radius) {
this.dx = -this.dx
} else if (this.x <= 0) {
this.dx = -this.dx
}
if (this.y >= innerHeight - this.radius) {
this.dy = -this.dy
} else if (this.y <= 0) {
this.dy = -this.dy
}
this.x += this.dx
this.y += this.dy
this.draw()
}
}
}
function Rectangle(x, y, dx, dy, height, width, color) {
this.x = x
this.y = y
this.dx = dx
this.dy = dy
this.height = height
this.width = width
this.color = color
this.draw = function() {
c.beginPath()
c.fillStyle = this.color
c.fillRect(this.x, this.y, this.width, this.height)
}
this.update = function() {
repeattwo += 1
if (changeDirection === true && repeattwo <= 1) {
this.dx = -this.dx
this.dy = -this.dy
if (this.x >= innerWidth - this.width) {
this.dx = -this.dx
} else if (this.x <= 0) {
this.dx = -this.dx
}
if (this.y >= innerHeight - this.height) {
this.dy = -this.dy
} else if (this.y <= 0) {
this.dy = -this.dy
}
this.x += this.dx
this.y += this.dy
this.draw()
} else if (changeDirection === false || repeattwo > 1) {
if (this.x >= innerWidth - this.width) {
this.dx = -this.dx
} else if (this.x <= 0) {
this.dx = -this.dx
}
if (this.y >= innerHeight - this.height) {
this.dy = -this.dy
} else if (this.y <= 0) {
this.dy = -this.dy
}
this.x += this.dx
this.y += this.dy
this.draw()
}
}
}
// let x = Math.floor((Math.random() * innerWidth) + 1)
// let dx = Math.floor((Math.random() * 10) + 1)
// let y = Math.floor((Math.random() * innerHeight) + 1)
// let dy = Math.floor((Math.random() * 10) + 1)
//
// console.log("X Value " + x)
// console.log("Y Value " + y)
// console.log("X Velocity Value " + dx)
// console.log("Y Velocity Value " + dy)
//
let rectangleArray = []
let circleArray = []
for (var i = 0; i < 50; i++) {
let xRect = Math.floor((Math.random() * innerWidth) + 1)
let dxRect = Math.floor((Math.random() * 10) + 1)
let yRect = Math.floor((Math.random() * innerHeight) + 1)
let dyRect = Math.floor((Math.random() * 10) + 1)
let heightRect = Math.floor((Math.random() * 250) + 1)
let widthRect = Math.floor((Math.random() * 250) + 1)
let colorRect = random_rgba()
let xCircle = Math.floor((Math.random() * innerWidth) + 1)
let dxCircle = Math.floor((Math.random() * 10) + 1)
let yCircle = Math.floor((Math.random() * innerHeight) + 1)
let dyCircle = Math.floor((Math.random() * 10) + 1)
let heightCircle = Math.floor((Math.random() * 250) + 1)
let widthCircle = Math.floor((Math.random() * 250) + 1)
let colorCircle = random_rgba()
let radiusCircle = Math.floor((Math.random() * 100) + 1)
rectangleArray.push(new Rectangle(xRect, yRect, dxRect, dyRect, heightRect, widthRect, colorRect))
circleArray.push(new Circle(xCircle, yCircle, dxCircle, dyCircle, radiusCircle, colorCircle))
}
console.log(circleArray)
console.log(rectangleArray)
function animate() {
requestAnimationFrame(animate)
c.clearRect(0, 0, innerWidth, innerHeight)
for (var i = 0; i < rectangleArray.length; i++) {
rectangleArray[i].draw()
rectangleArray[i].update()
}
for (var i = 0; i < circleArray.length; i++) {
circleArray[i].draw()
circleArray[i].update()
}
}
animate()
<canvas></canvas>
You had a lot of code, I remove a lot of it...
Take a look at the new logic under the addEventListener
Troubleshooting cases like this the first thing I do is reduce the scope:
you had 100 figures, I took it down to 6
your animation was too fast, I reduced the speed.
lot's of random stuff on the code that created the figures, I hard-coded some of that.
you had changeDirection, repeatone & repeattwo, I could not figure out what you needed those for so I remove them.
let canvas = document.querySelector("canvas")
canvas.width = canvas.height = innerWidth = innerHeight = 300
let c = canvas.getContext("2d")
window.addEventListener('keydown', function(event) {
for (var i = 0; i < rectangleArray.length; i++) {
rectangleArray[i].dx *= -1
rectangleArray[i].dy *= -1
}
for (var i = 0; i < circleArray.length; i++) {
circleArray[i].dx *= -1
circleArray[i].dy *= -1
}
})
function random_rgba() {
var o = Math.round,
r = Math.random,
s = 255;
return 'rgba(' + o(r() * s) + ',' + o(r() * s) + ',' + o(r() * s) + ',' + r().toFixed(1) + ')';
}
// c.arc(xCircle, yCircle, radius, 0, Math.PI * 2, false)
function Circle(x, y, dx, dy, radius, color) {
this.x = x
this.y = y
this.dx = dx
this.dy = dy
this.radius = radius
this.color = color
this.draw = function() {
// c.strokeStyle = this.color
c.beginPath()
c.strokeStyle = this.color
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false)
c.stroke()
}
this.update = function() {
if (this.x >= innerWidth - this.radius) {
this.dx = -this.dx
} else if (this.x <= this.radius) {
this.dx = -this.dx
}
if (this.y >= innerHeight - this.radius) {
this.dy = -this.dy
} else if (this.y <= this.radius) {
this.dy = -this.dy
}
this.x += this.dx
this.y += this.dy
this.draw()
}
}
function Rectangle(x, y, dx, dy, height, width, color) {
this.x = x
this.y = y
this.dx = dx
this.dy = dy
this.height = height
this.width = width
this.color = color
this.draw = function() {
c.beginPath()
c.fillStyle = this.color
c.fillRect(this.x, this.y, this.width, this.height)
}
this.update = function() {
if (this.x >= innerWidth - this.width) {
this.dx = -this.dx
} else if (this.x <= 0) {
this.dx = -this.dx
}
if (this.y >= innerHeight - this.height) {
this.dy = -this.dy
} else if (this.y <= 0) {
this.dy = -this.dy
}
this.x += this.dx
this.y += this.dy
this.draw()
}
}
function randSpeed() {
return (Math.random() - 0.5) * 5
}
let rectangleArray = []
let circleArray = []
for (var i = 0; i < 3; i++) {
let heightRect = Math.floor((Math.random() * 100) + 1)
let widthRect = Math.floor((Math.random() * 100) + 1)
rectangleArray.push(new Rectangle(80, 80, randSpeed(), randSpeed(), heightRect, widthRect, random_rgba()))
let radiusCircle = Math.floor((Math.random() * 50) + 1)
circleArray.push(new Circle(80, 80, randSpeed(), randSpeed(), radiusCircle, random_rgba()))
}
function animate() {
requestAnimationFrame(animate)
c.clearRect(0, 0, innerWidth, innerHeight)
for (var i = 0; i < rectangleArray.length; i++) {
rectangleArray[i].draw()
rectangleArray[i].update()
}
for (var i = 0; i < circleArray.length; i++) {
circleArray[i].draw()
circleArray[i].update()
}
}
animate()
<canvas></canvas>
The ball seems to bounce off one side of the paddle, but when it comes from the side it glitches through the paddle. I just can't find a way behind it and it really bothers me. I am using some logic gates to define where the ball's direction is need to be invereted
function startGame() {
GameArea.start();
Ball1 = new CircleComp('white' , window.innerWidth - 200 , window.innerHeight - 20);
Ball1.ySpeed = 13.5;
Ball1.xSpeed = 6;
Paddle1 = new PaddleComp( 87, 83, 0, window.innerHeight / 2.5, 10, 70);
Paddle2 = new PaddleComp( 38, 40, window.innerWidth - 10, window.innerHeight / 2.5, 10 , 70);
}
var GameArea = {
canvas : canvas = document.querySelector("canvas"),
start : function (){
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
this.ctx = this.canvas.getContext('2d');
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
GameArea.keys = (GameArea.keys || []);
GameArea.keys[e.keyCode] = true;
})
window.addEventListener('keyup', function (e) {
GameArea.keys[e.keyCode] = false;
})
},
clear : function() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function CircleComp(color, x , y){
this.x = x;
this.y = y;
this.width = 8;
this.height = 8;
var context1 = GameArea.ctx;
this.update = function(){
context1.beginPath();
context1.fillStyle = color;
context1.fillRect(this.x, this.y, this.width, this.height);
context1.fill();
context1.stroke();
this.updatePosition();
}
this.updatePosition = function(){
this.y += this.ySpeed;
this.x += this.xSpeed;
if(this.x + this.width > GameArea.canvas.width){
this.xSpeed = -this.xSpeed;
}
if(this.y + this.height > GameArea.canvas.height){
this.ySpeed = -this.ySpeed;;
}
if(this.x - this.width < 0){
this.xSpeed = -this.xSpeed;
}
if(this.y - this.height < 0){
this.ySpeed = -this.ySpeed;
}
if(this.y + this.height > Paddle2.y && this.y - this.width < (Paddle2.y + 130) && this.x + this.width > Paddle2.x ){
this.xSpeed = -this.xSpeed;
}
if(this.y + this.height > Paddle1.y && this.y - this.width < (Paddle1.y + 70) && this.x - this.height < Paddle1.x + 10){
this.xSpeed = -this.xSpeed;
}
}
}
function PaddleComp(Upkey, Downkey, x, y, width, height){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.ySpeed = 0;
var context2 = GameArea.ctx;
this.update = function(){
context2.fillStyle = 'white';
context2.fillRect(x,this.y,this.width,this.height);
this.updatePosition();
}
this.updatePosition = function() {
this.ySpeed = 0;
if (GameArea.keys && GameArea.keys[Upkey]) {
this.ySpeed = -15; //console.log('Up');
}
if (GameArea.keys && GameArea.keys[Downkey]) {
this.ySpeed = 15; //console.log('Down');
}
if ((GameArea.keys && GameArea.keys[Downkey]) && this.y + 130 > window.innerHeight){
this.ySpeed = this.ySpeed -15 ;
}
if ((GameArea.keys && GameArea.keys[Upkey]) && this.y < 0 ){
this.ySpeed = this.ySpeed +15 ;
}
this.y += this.ySpeed;
}
}
function updateGameArea(){
GameArea.clear();
Paddle1.update();
Paddle2.update();
Ball1.update();
}
<html>
<head>
<meta charset='urf-8'>
<style>
canvas{
border: 0px solid black;
background-color: black;
}
body{
margin: 0;
overflow: hidden;
}
</style>
</head>
<body onload='startGame()'>
<canvas></canvas>
<script src='Pong.js'></script>
</body>
</html>
Could not see directly what the problem was with your code so i just rewrote the code with the ball, bat (paddle) test function in the ball Object and called from the players object. ball.checkPad(player); tests if the ball has hit the players bat. To help picture what is happening I have slowed it all down and made the bats real phat. When the ball hits the bat it will turn yellow and the bat red for a second or so.
There is plenty of comments in the parts you asked about,
Hope it helps
Demo copied from OP question.
const setting = {
speed : 2, // of ball
left : 0,
width : 400,
height : 200,
padWidth : 50,
padHeight : 80,
padSpeed : 4, // double balls
hitPauseCount : 30, // nuber of frames to hold when there is a collisiotn so you
// can check all is good
}
const keys = {
ArrowUp : false,
ArrowDown : false,
ArrowLeft : false,
ArrowRight : false,
keyEvent(e) { // dont use keyCode it has depreciated
if (keys[e.code] !== undefined) {
keys[e.code] = e.type === "keydown";
e.preventDefault();
}
}
}
var ctx;
var ball1, paddle1, paddle2;
var gameArea = {
start() {
canvas.width = setting.width;
canvas.height = setting.height;
ctx = canvas.getContext('2d');
requestAnimationFrame(updateGameArea);
},
clear() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
}
}
gameArea.start();
ball = new CircleComp('white', window.innerWidth - 200, window.innerHeight - 20);
ball.ySpeed = setting.speed;
ball.xSpeed = setting.speed;
paddle1 = new PaddleComp("ArrowUp", "ArrowDown", setting.left, setting.height / 2, setting.padWidth, setting.padHeight);
paddle2 = new PaddleComp("ArrowLeft", "ArrowRight", setting.width - setting.padWidth, setting.height / 2, setting.padWidth, setting.padHeight);
window.addEventListener('keydown', keys.keyEvent);
window.addEventListener('keyup', keys.keyEvent);
function CircleComp(color, x, y) {
this.x = x;
this.y = y;
this.width = 8;
this.height = 8;
this.xSpeed = setting.speed;
var hit = 0;
var restartCount;
var serveDirection;
this.reset = function(){
this.x = ctx.canvas.width /2;
this.y = ctx.canvas.height / 2;
this.xSpeed = -this.xSpeed
this.ySpeed = setting.speed * Math.sign(Math.random() - 0.5);
restartCount = 60;
}
this.draw = function () {
if(hit > 0){
hit -= 1;
ctx.fillStyle = "yellow";
}else{
ctx.fillStyle = color;
}
ctx.fillRect(this.x, this.y, this.width, this.height);
}
// next funtion is called by the player objects
this.checkPad = function (player) {
if (player.x > canvas.width / 2) { // is player on left or right
if (this.xSpeed > 0) { // player on right only check if ball moving rigth
if (this.x + this.width > player.x) { // ball is in paddles zone
//if not bottom of ball above top of bat or top of ball bellow bottom of bat
if (!(this.y + this.height <= player.y || this.y >= player.y + player.height)) {
// ball and bat in contact
// is ball moving down and the balls top edge above the player
// then ball has hit the top side of the bat
if(this.ySpeed > 0 && this.y <= player.y){
this.y = player.y - this.width;
this.ySpeed = -setting.speed;
}else if(this.ySpeed < 0 && this.y + this.height >= player.y + player.height){ // do bottom check
this.y = player.y + player.height;
this.ySpeed = setting.speed;
}else{ // ball hit front of bat
this.x = player.x - this.width;
this.xSpeed = - setting.speed;
}
player.hit = setting.hitPauseCount; // counters to show FX when a hit happens
hit = setting.hitPauseCount;
}
}
}
} else { // player must be left
if (this.xSpeed < 0) { // ball must move left
if (this.x < player.x + player.width) { // ball is in paddles zone
if (!(this.y + this.height <= player.y || this.y >= player.y + player.height)) {
// ball and bat in contact
// ball and bat in contact
// is ball moving down and the balls top edge above the player
// then ball has hit the top side of the bat
if(this.ySpeed > 0 && this.y <= player.y){
this.y = player.y - this.width;
this.ySpeed = -setting.speed;
}else if(this.ySpeed < 0 && this.y + this.height >= player.y + player.height){ // do bottom check
this.y = player.y + player.height;
this.ySpeed = setting.speed;
}else{ // ball hit front of bat
this.x = player.x + player.width;
this.xSpeed = setting.speed;
}
player.hit = setting.hitPauseCount; // counters to show FX when a hit happens
hit = setting.hitPauseCount;
}
}
}
}
}
this.update = function () {
if(restartCount > 0){ // wait for restart pause
restartCount -= 1;
}else{
if(hit > 0){ // do nothing if paused
return;
}
this.y += this.ySpeed;
this.x += this.xSpeed;
if (this.x + this.width >= canvas.width) {
this.reset(); // point
} else if (this.x < 0) {
this.reset(); // point
}
if (this.y + this.height >= canvas.height) {
this.y = canvas.height - this.height;
this.ySpeed = -setting.speed;
} else if (this.y < 0) {
this.y = 0;
this.ySpeed = setting.speed;
}
}
}
this.reset();
}
function PaddleComp(upKey, downKey, x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.hit = 0;
this.draw = function () {
if(this.hit > 0){
this.hit -= 1;
ctx.fillStyle = "red";
}else{
ctx.fillStyle = '#9CF';
}
ctx.fillRect(this.x, this.y, this.width, this.height);
}
this.update = function () {
if (keys[upKey]) {
this.y -= setting.padSpeed;
};
if (keys[downKey]) {
this.y += setting.padSpeed;
};
if (this.y < 0) {
this.y = 0;
}
if (this.y + this.height >= canvas.height) {
this.y = canvas.height - this.height;
}
ball.checkPad(this);
}
}
function updateGameArea() {
gameArea.clear();
paddle1.update();
paddle2.update();
ball.update();
paddle1.draw();
paddle2.draw();
ball.draw();
requestAnimationFrame(updateGameArea);
}
<canvas id=canvas style='background:#69C;border:2px blue solid'></canvas>
I recently made a JS Pong game. It works well, but the ball rarely gets stuck at the bottom or top. It looks like it is halfway through the wall and constantly bouncing. Video of the issue happening. You can try the game here. I do not know why this issue is happening because the logic seems right and works 90% of the time correctly. Here are the main two functions of my program:
function moveAll() {
if (showingWinScreen) {
return;
}
computerMovement();
ballX += ballSpeedX;
ballY += ballSpeedY;
if (ballY <= 10) {
ballSpeedY = -ballSpeedY;
} else if (ballY >= HEIGHT - 10) {
ballSpeedY = -ballSpeedY;
}
if (ballX >= WIDTH - 10) {
if ((ballY > paddleY) && (ballY < paddleY + 100)) {
ballSpeedX = -ballSpeedX;
var deltaY = ballY - paddleY - 50;
ballSpeedY = deltaY / 5;
} else {
player1Score++;
ballReset();
}
} else if (ballX <= 10) {
if ((ballY > mouseY - 50) && (ballY < mouseY + 50)) {
ballSpeedX = -ballSpeedX;
deltaY = ballY - mouseY;
ballSpeedY = deltaY / 6;
} else {
player2Score++;
ballReset();
}
}
}
function drawAll() {
if (showingWinScreen) {
colorRect(0, 0, WIDTH, HEIGHT, "black");
canvas.fillStyle = "yellow";
canvas.fillText("Click to continue!", 300, 300);
if (player1Score == WINNING_SCORE) {
canvas.fillText("You won!", 360, 500);
} else if (player2Score == WINNING_SCORE) {
canvas.fillText("The computer beat you!", 280, 500);
}
return;
}
colorRect(0, 0, WIDTH, HEIGHT, "black");
drawNet();
makeCircle(ballX, ballY, 10, 0, Math.PI * 2, "red");
colorRect(790, paddleY, 10, 100, "cyan");
colorRect(0, mouseY - 50, 10, 100, "yellow");
canvas.fillStyle = "white";
canvas.fillText(player1Score + " " + player2Score, 360, 100);
}
Thank you for your help!
I think there's only one case in which this could happen: when, in a colliding frame, you decrease the speed.
When the speed remains the same, no matter what, your ball will always bounce back to the previous' frames position:
var cvs = document.querySelector("canvas");
var ctx = cvs.getContext("2d");
var balls = [
Ball(50, 50, 0, 5, 5, "red"),
Ball(100, 50, 0, 5, 10, "blue"),
Ball(150, 50, 0, 5, 15, "green"),
Ball(200, 50, 0, 5, 20, "yellow")
];
var next = () => {
updateFrame(balls);
drawFrame(balls);
}
var loop = () => {
requestAnimationFrame(() => {
next();
loop();
});
}
next();
function Ball(x, y, vx, vy, r, color) {
return {
x: x,
y: y,
vx: vx,
vy: vy,
r: r,
color: color
}
};
function updateBall(b) {
b.x += b.vx;
b.y += b.vy;
if (b.y <= b.r ||
b.y >= cvs.height - b.r) {
b.vy *= -1;
}
};
function drawBall(b) {
ctx.beginPath();
ctx.fillStyle = b.color;
ctx.arc(b.x, b.y, b.r, 0, 2 * Math.PI, false);
ctx.fill();
}
function updateFrame(balls) {
balls.forEach(updateBall);
}
function drawFrame(balls) {
ctx.clearRect(0, 0, cvs.width, cvs.height);
balls.forEach(drawBall);
};
<canvas width="300" height="150" style="background: #454545"></canvas>
<button onclick="next()">next</button>
<button onclick="loop()">run</button>
But when the speed changes, things get stuck:
var cvs = document.querySelector("canvas");
var ctx = cvs.getContext("2d");
var balls = [
Ball(50, 50, 0, 10, 5, "red"),
Ball(100, 50, 0, 10, 10, "blue"),
Ball(150, 50, 0, 10, 15, "green"),
Ball(200, 50, 0, 10, 20, "yellow")
];
var next = () => {
updateFrame(balls);
drawFrame(balls);
}
var loop = () => {
requestAnimationFrame(() => {
next();
loop();
});
}
next();
function Ball(x, y, vx, vy, r, color) {
return {
x: x,
y: y,
vx: vx,
vy: vy,
r: r,
color: color
}
};
function updateBall(b) {
b.x += b.vx;
b.y += b.vy;
if (b.y <= b.r ||
b.y >= cvs.height - b.r) {
b.vy *= -0.5;
}
};
function drawBall(b) {
ctx.beginPath();
ctx.fillStyle = b.color;
ctx.arc(b.x, b.y, b.r, 0, 2 * Math.PI, false);
ctx.fill();
}
function updateFrame(balls) {
balls.forEach(updateBall);
}
function drawFrame(balls) {
ctx.clearRect(0, 0, cvs.width, cvs.height);
balls.forEach(drawBall);
};
<canvas width="300" height="150" style="background: #454545"></canvas>
<button onclick="next()">next</button>
<button onclick="loop()">run</button>
In your case, I'm thinking this can only happen when there's a paddle collision AND a wall collision simultaneously.
A quick-to-implement solution would be to check if the new position is valid before translating the ball position. If you don't want the precise location, you can place the ball at the point of collision. Note that this will produce a slightly off frame.
E.g.:
var newY = ballY + ballSpeedY;
// Top wall
if(newY <= 10) {
ballY = 10;
ballSpeedY = -ballSpeedY;
}
// Bottom wall
else if(newY >= HEIGHT-10){
ballY = HEIGHT - 10;
ballSpeedY = -ballSpeedY;
}
// No collision
else {
ballY = newY;
}
Update: a more detailed description of what can happen
Let's say your ball collides with the top border of your canvas and with your paddle in the same frame.
First, you move the ball to the colliding position: ballY += ballSpeedY; Say your ballY is 4, and your ballSpeedY is -5, you'll position the ball to -1, inside the wall.
If this were to be the only collision, you should be okay. You flip the speed (ballSpeedY = -ballSpeedY), so in the next frame, your ball should be back at -1 + 5 = 4, so ballY will be 4 again, and your ball will move towards 4 + 5 = 9 in the next frame.
Now a problem arises, when in the -1 positioned frame, you collide with the paddle as well! When the paddle hits the ball, you modify the ballspeed: ballSpeedY = deltaY / 5;. If this turns out to be < 1, your ball won't be able to exit the wall in the next frame. Instead of -1 + 5 = 4, your ball will, for example, move to: -1 + 0.5 = -0.5.
Now, your ball won't be able to get back in to play, since the next frame will, again, calculate a collision and flip the speed. This results in the bouncy, trembling effect you see when the ball gets stuck.
A naive but pretty decent solution, is to only update the position of the ball to a valid position. I.e.: never to a colliding coordinate.
var animate = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) {
window.setTimeout(callback, 1000 / 60)
};
var canvas = document.createElement("canvas");
var width = 400;
var height = 600;
canvas.width = width;
canvas.height = height;
var context = canvas.getContext('2d');
var player = new Player();
var computer = new Computer();
var ball = new Ball(200, 300);
var keysDown = {};
var render = function () {
context.fillStyle = "#FF00FF";
context.fillRect(0, 0, width, height);
player.render();
computer.render();
ball.render();
};
var update = function () {
player.update();
computer.update(ball);
ball.update(player.paddle, computer.paddle);
};
var step = function () {
update();
render();
animate(step);
};
function Paddle(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.x_speed = 0;
this.y_speed = 0;
}
Paddle.prototype.render = function () {
context.fillStyle = "#0000FF";
context.fillRect(this.x, this.y, this.width, this.height);
};
Paddle.prototype.move = function (x, y) {
this.x += x;
this.y += y;
this.x_speed = x;
this.y_speed = y;
if (this.x < 0) {
this.x = 0;
this.x_speed = 0;
} else if (this.x + this.width > 400) {
this.x = 400 - this.width;
this.x_speed = 0;
}
};
function Computer() {
this.paddle = new Paddle(175, 10, 50, 10);
}
Computer.prototype.render = function () {
this.paddle.render();
};
Computer.prototype.update = function (ball) {
var x_pos = ball.x;
var diff = -((this.paddle.x + (this.paddle.width / 2)) - x_pos);
if (diff < 0 && diff < -4) {
diff = -5;
} else if (diff > 0 && diff > 4) {
diff = 5;
}
this.paddle.move(diff, 0);
if (this.paddle.x < 0) {
this.paddle.x = 0;
} else if (this.paddle.x + this.paddle.width > 400) {
this.paddle.x = 400 - this.paddle.width;
}
};
function Player() {
this.paddle = new Paddle(175, 580, 50, 10);
}
Player.prototype.render = function () {
this.paddle.render();
};
Player.prototype.update = function () {
for (var key in keysDown) {
var value = Number(key);
if (value == 37) {
this.paddle.move(-4, 0);
} else if (value == 39) {
this.paddle.move(4, 0);
} else {
this.paddle.move(0, 0);
}
}
};
function Ball(x, y) {
this.x = x;
this.y = y;
this.x_speed = 0;
this.y_speed = 3;
}
Ball.prototype.render = function () {
context.beginPath();
context.arc(this.x, this.y, 5, 2 * Math.PI, false);
context.fillStyle = "#000000";
context.fill();
};
Ball.prototype.update = function (paddle1, paddle2) {
this.x += this.x_speed;
this.y += this.y_speed;
var top_x = this.x - 5;
var top_y = this.y - 5;
var bottom_x = this.x + 5;
var bottom_y = this.y + 5;
if (this.x - 5 < 0) {
this.x = 5;
this.x_speed = -this.x_speed;
} else if (this.x + 5 > 400) {
this.x = 395;
this.x_speed = -this.x_speed;
}
if (this.y < 0 || this.y > 600) {
this.x_speed = 0;
this.y_speed = 3;
this.x = 200;
this.y = 300;
}
if (top_y > 300) {
if (top_y < (paddle1.y + paddle1.height) && bottom_y > paddle1.y && top_x < (paddle1.x + paddle1.width) && bottom_x > paddle1.x) {
this.y_speed = -3;
this.x_speed += (paddle1.x_speed / 2);
this.y += this.y_speed;
}
} else {
if (top_y < (paddle2.y + paddle2.height) && bottom_y > paddle2.y && top_x < (paddle2.x + paddle2.width) && bottom_x > paddle2.x) {
this.y_speed = 3;
this.x_speed += (paddle2.x_speed / 2);
this.y += this.y_speed;
}
}
};
document.body.appendChild(canvas);
animate(step);
window.addEventListener("keydown", function (event) {
keysDown[event.keyCode] = true;
});
window.addEventListener("keyup", function (event) {
delete keysDown[event.keyCode];
});
http://jsfiddle.net/kHJr6/2/
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. :)
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>