2d moving squares collision detection - javascript

There are two squares, they are moved with the arrows and asdw, and I have been trying to get this collision detection down for ages, it's killing me. Anyways, I have tried so many methods, I got really really close with one method, and everything was fine with it, until I found out that when you jumped next to it, it didn't complete the full jump because one of the sides was able to go through the other square. After a long time, I found out that method wouldn't work because with GetContext.2d, you cannot reference the bottom left of a square. There also was the issue that one corner could touch two different sides, which would set off conflicting messages. My solution for that was to make a third condition to specify which side the corner was touching, weather the top/bottom or left/right. I made a model of how the code would be structured:
The code I have currently looks like this:
const context = document.getElementById("canvasmulti").getContext("2d");
canvasmulti.width = window.innerWidth;
canvasmulti.height = window.innerHeight;
//CHARACTER:
const square = {
height: 75,
jumping: true,
width: 75,
x: canvasmulti.width - 75,
xVelocity: 0,
y: canvasmulti.height / 2,
yVelocity: 0,
jumpHeight: 30
};
const square2 = {
height: 75,
jumping: true,
width: 75,
x: 0,
xVelocity: 0,
y: canvasmulti.height / 2,
yVelocity: 0,
jumpHeight: 30
};
//MOVEMENT:
const controller = {
left: false,
right: false,
up: false,
keyListener: function (event) {
let key_state = (event.type == "keydown") ? true : false;
switch (event.keyCode) {
case 37: // left arrow
controller.left = key_state;
break;
case 38: // up arrow
controller.up = key_state;
break;
case 39: // right arrow
controller.right = key_state;
break;
}
}
};
const controller2 = {
left: false,
right: false,
up: false,
keyListener: function (event) {
let key_state = (event.type == "keydown") ? true : false;
switch (event.keyCode) {
case 65: // left arrow
controller2.left = key_state;
break;
case 87: // up arrow
controller2.up = key_state;
break;
case 68: // right arrow
controller2.right = key_state;
break;
}
}
};
const loop = function () {
//controller one
if (controller.up && square.jumping == false) {
square.yVelocity -=square.jumpHeight;
square.jumping = true;}
if (controller.left) {
square.xVelocity -= 0.5;}
if (controller.right) {
square.xVelocity += 0.5;}
//controller two
if (controller2.up && square2.jumping == false) {
square2.yVelocity -= square2.jumpHeight;
square2.jumping = true;}
if (controller2.left) {
square2.xVelocity -= 0.5;}
if (controller2.right) {
square2.xVelocity += 0.5;}
//controller one
square.yVelocity += 1.5;// gravity
square.x += square.xVelocity;
square.y += square.yVelocity;
square.xVelocity *= 0.9;// friction
square.yVelocity *= 0.9;// friction
//controller two
square2.yVelocity += 1.5;// gravity
square2.x += square2.xVelocity;
square2.y += square2.yVelocity;
square2.xVelocity *= 0.9;// friction
square2.yVelocity *= 0.9;// friction
// if square1 is falling below floor line
if (square.y > canvasmulti.height - 75) {
square.jumping = false;
square.y = canvasmulti.height - 75;
square.yVelocity = 0;
}
// if square2 is falling below floor line
if (square2.y > canvasmulti.height - 75) {
square2.jumping = false;
square2.y = canvasmulti.height - 75;
square2.yVelocity = 0;
}
// if square1 is going off the left of the screen
if (square.x < 0) {
square.x = 0;
} else if (square.x > canvasmulti.width - 75) {// if square goes past right boundary
square.x = canvasmulti.width - 75;
}
// if square2 is going off the left of the screen
if (square2.x < 0) {square2.x = 0;}
else if (square2.x > canvasmulti.width - 75) {// if square goes past right boundary
square2.x = canvasmulti.width - 75;
}
// Creates the backdrop for each frame
context.fillStyle = "#394129";
context.fillRect(0, 0, canvasmulti.width, canvasmulti.height); // x, y, width, height
// Creates and fills square1 for each frame
context.fillStyle = "#8DAA9D"; // hex for cube color
context.beginPath();
context.rect(square.x, square.y, square.width, square.height);
context.fill();
// Creates and fills square2 for each frame
context.fillStyle = "#781818"; // hex for cube color
context.beginPath();
context.rect(square2.x, square2.y, square2.width, square2.height);
context.fill();
// Collision detection of squares
if (square.x <= square2.x + square2.width && square.x >= square2.x &&
square.y >= square2.y && square.y <= square2.y + square2.height &&
square2.y + square2.height >= square.y && square2.y + square2.height <= square.y + square.height)
{square.y = square2.y - square.height; //set it to a position where they don't overlap
square.yVelocity = 0;}; //One move down
if (square.x <= square2.x + square2.width && square.x >= square2.x &&
square.y >= square2.y && square.y <= square2.y + square2.height &&
square2.x + square2.width >= square.x && square2.x + square2.width <= square.x + square.width);
{square.x = square2.x + square2.width; // set it to a position where they don't overlap
square.xVelocity = 0;}; //One move right
if (square2.x >= square.x && square2.x <= square.x + square.width &&
square2.y >= square.y && square2.y <= square.y + square.heights &&
square.y + square.height >= square2.y && square.y + square.height <= square2.y + square2.height)
{square.y = square2.y - square.height; // set it to a position where they don't overlap
square.yVelocity = 0;}; //One move up
if (square2.x >= square.x && square2.x <= square.x + square.width &&
square2.y >= square.y && square2.y <= square.y + square.heights &&
square.x + square.width >= square2.y && square.x + square.width <= square2.y + square2.height)
{square.x = square2.x - square.width; // set it to a position where they don't overlap
square.xVelocity = 0;}; //One move left
// call update when the browser is ready to draw again
window.requestAnimationFrame(loop);
};
//square1
window.addEventListener("keydown", controller.keyListener)
window.addEventListener("keyup", controller.keyListener);
//square2
window.addEventListener("keydown", controller2.keyListener)
window.addEventListener("keyup", controller2.keyListener);
window.requestAnimationFrame(loop);
<!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>ToneMain</title>
<style>
body {
height:100vh;
width:100vh;
margin: 0;
}
</style>
</head>
<body>
<canvas id="canvasmulti"></canvas>
<script src="ToneMainMulti.js"></script>
</body>
</html>
For the love of my sanity this was the last time in a month I tried to fix this. This was my last blow. I would really apprestiate any effort or advice to solve my problem, this is tough to wrap your head around and may take a long time, thxxx <3

I feel your pain. CD is not as easy as one would hope. I was unable to get your code working so I rewrote it using ES6 classes because I like them. I also don't put everything into a loop function but rather place controlling parameters into separate functions and call those functions in the animate loop. Hopefully it isn't confusing from how you write.
The CD portion calculates the distance between the different sides of the blocks and determines which sides from each block are the closest and will collide. By doing this only one function will get called at a time. This prevents any of the unwanted behavior you get because of the blocks having a slight overlap and causing other functions to trigger also. i.e. colliding on the X but having the Y trigger too. I've also set it to where if the blocks are stacked only the top one can jump.
You can tailor this to whatever suits your needs.
const canvas = document.getElementById('canvasmulti');
const context = canvas.getContext('2d');
canvas.width = 500;
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;
}
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(0, 0, 0, 0, 'red', false); //must have separate variables like jump/attack etc
let player2 = new Player(canvasmulti.width - 50, 0, 0, 0, 'blue', 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;
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' }
}
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' }
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
window.addEventListener('keydown', controller2);
window.addEventListener('keyup', controller2);
}
}
let controller1 = new Controller();
let controller2 = new Controller();
function collisionDetection(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 d*** 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 to collisionActions
collisionActions(obj, obj2, val)
}
function collisionActions(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;
obj.vy = 0;
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;
obj.vx = 0;
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;
obj2.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 loop() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
controlPlayer1(player1);
controlPlayer2(player2);
collisionDetection(player1, player2)
requestAnimationFrame(loop)
}
loop();
It may seem like a lot but it's really not and it works very well. I was having the same issues a while back and came up with this to use on a collision map because my character would randomly shoot of in the wrong direction or jumping next to a block wouldn't work. I have modified that code to be used here with just 2 moving blocks. Let me know if this works for you.

Related

how to fix bugs in snake game?

I have been making a snake game for a few days to improve my js skills.
And I released the first beta test of my mini-game. And you actually know that there are always a lot of bugs in beta tests. And I noticed them. First I notice that if the snake.x position < 0, then the snake appears from a different corner, but in front of one block, and not from the zero coordinate. This also applies to snake.y<0.
here is the code:
var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
var box = 10;
//Snake
var snake = [];
var dir = "right";
var maxCell = 10;
var can = canvas.getBoundingClientRect();
var px = Math.floor(canvas.width / 2 / 10) * 10;
var py = Math.floor(canvas.height / 2 / 10) * 10;
//Apple
var ax = Math.floor((Math.random() * ~~(canvas.width / box)) / 10) * 10;
var ay = Math.floor((Math.random() * ~~(canvas.height / box)) / 10) * 10;
var loop = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
Elems();
}, 1000 / 40);
document.addEventListener("keydown", function (e) {
if (e.keyCode === 37 && dir !== "right") {
dir = "left";
//console.log('left')
} else if (e.keyCode === 38 && dir !== "down") {
dir = "up";
//console.log('up')
} else if (e.keyCode === 39 && dir !== "left") {
dir = "right";
//console.log('right')
} else if (e.keyCode === 40 && dir !== "up") {
dir = "down";
//console.log('down')
}
});
function direction() {
if (dir == "right") {
px += box;
} else if (dir == "left") {
px -= box;
} else if (dir == "up") {
py -= box;
} else if (dir == "down") {
py += box;
}
}
//Closure )))
function Elems() {
function Apple() {
ctx.fillStyle = "green";
ctx.fillRect(ax, ay, box, box);
}
//! Spawn snake
function Snake() {
direction();
var head = {
x: px,
y: py,
};
snake.unshift(head);
//-------------BUG---------------
if (px >= canvas.width) {
px = 0;
}
if (px + box < 0) {
px = canvas.width;
}
if (py >= canvas.height) {
py = 0;
}
if (py + box < 0) {
py = canvas.height;
}
//-------------------------------
snake.forEach(function (elem, index) {
ctx.fillStyle = `red`;
ctx.fillRect(elem.x, elem.y, box, box);
});
if (head.x == ax && head.y == ay) {
maxCell += 1;
ax = Math.floor(Math.random() * ~~(canvas.width / box)) * 10;
ay = Math.floor(Math.random() * ~~(canvas.height / box)) * 10;
}
for (let i = 1; i < snake.length; i++) {
if (head.x === snake[i].x && head.y === snake[i].y) {
maxCell = 10;
px = Math.floor(canvas.width / 2 / 10) * 10;
py = Math.floor(canvas.height / 2 / 10) * 10;
clearInterval(loop);
alert("Game Over:(")
}
}
if (snake.length < maxCell) {
snake.push({ x: px, y: py });
}
snake.pop();
}
Snake();
Apple();
}
I tried changing the canvas dimensions and box value. But it didn't help x((((
You'd have to rearrange your code a good bit to get it working right.
The Snake function should not happen in a loop, the loop related stuff inside of Snake should be inside the Elems function instead. And the loop being set should happen last.
The following is a basic rework of your code with an added method, and I've included the grid that angel.bonev suggested so it works like a traditional snake game now and with the apple getting eaten to increase the snake size. The testForPointInArea method makes it such that the snake does not have to be exactly over the apple spot, but just overlapping.
Also the opposite direction restrictions in the keydown listener were removed;
&& dir !== "right" && dir !== "down" && dir !== "left" && dir !== "up"
If the snake is a small size or not big enough to bank corners,
then game over would not happen with the above backwards restrictions.
var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
var box = 10;
//Snake body Array
var snake = [];
var dir = "right";
var maxCell = 2;
var can = canvas.getBoundingClientRect();
var grid = {
x: Math.floor(canvas.width / box),
y: Math.floor(canvas.height / box)
};
var px = Math.floor(grid.x / 2) * 10;
var py = Math.floor(grid.y / 2) * 10;
//Apple cords
var ax = Math.floor( Math.random() * grid.y ) * box;
var ay = Math.floor( Math.random() * grid.x ) * box;
var theSnake;
document.addEventListener("keydown", function(e) {
e.preventDefault();
if (e.keyCode === 37 ) {
dir = "left";
//console.log('left')
} else if (e.keyCode === 38 ) {
dir = "up";
//console.log('up')
} else if (e.keyCode === 39) {
dir = "right";
//console.log('right')
} else if (e.keyCode === 40 ) {
dir = "down";
//console.log('down')
}
});
function direction() {
if (dir == "right") {
px += 2;
} else if (dir == "left") {
px -= 2;
} else if (dir == "up") {
py -= 2;
} else if (dir == "down") {
py += 2;
}
}
function Apple() {
ctx.fillStyle = "green";
ctx.fillRect(ax, ay, box, box);
}
function testForPointInArea(p, left,top,right,bottom) {
return Boolean(!(p.x < left || p.x > right || p.y < top || p.y > bottom));
}
function Elems() {
direction();
theSnake.head.x = px; theSnake.head.y = py;
snake.unshift({x:theSnake.head.x, y:theSnake.head.y});
for (let i = 1; i < snake.length; i++) {
if (theSnake.head.x === snake[i].x && theSnake.head.y === snake[i].y) {
maxCell = 2;
px = Math.floor(grid.x / 2) * 10;
py = Math.floor(grid.y / 2) * 10;
snake = [];
//clearInterval(loop);
alert("Game Over :(");
return;
}
}
if (snake.length < maxCell) {
snake.push({ x: px, y: py });
}
if (px >= canvas.width) {
px = 0;
}
if (px + box < 0) {
px = canvas.width;
}
if (py >= canvas.height) {
py = 0;
}
if (py + box < 0) {
py = canvas.height;
}
theSnake.drawSnake();
Apple();
snake.pop();
if ( testForPointInArea(theSnake.head, ax - 3, ay - 3, ax + box + 3, ay + box + 3) ) {
maxCell += 1;
ax = Math.floor( Math.random() * grid.y ) * box;
ay = Math.floor( Math.random() * grid.x ) * box;
Apple();
}
}
//Snake Class
function Snake() {
this.head = {
x: px,
y: py,
};
this.drawSnake = function() {
snake.forEach(function (elem, index) {
ctx.fillStyle = "red";
ctx.fillRect(elem.x, elem.y, box, box);
});
};
}
theSnake = new Snake();
var loop = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
Elems();
}, 1000 / 40);
<canvas id="game" width="150px" height="150px"> </canvas>

2d Square to rectangle collision detection and action(physics)

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();

requestAnimationFrame looping causes velocity to increase when it says it's 0

This is the best way I could frame the question. I have two squares on the screen, and am trying to program collision detection. This is vanilla javascript and my code has detailed comments, please take a look, I have been stuck on this for some time.
To be specific, I set the square.yVelocity to 0 when touching any point within the top width of square2(also did this with x and sides). Even with this being set, and showing in the console when logged, square continues to move through square2.
Here is my code:
const context = document.getElementById("canvasmulti").getContext("2d");
canvasmulti.width = window.innerWidth;
canvasmulti.height = window.innerHeight;
//CHARACTER:
const square = {
height: 75,
jumping: true,
width: 75,
x: canvasmulti.width - 75,
xVelocity: 0,
y: canvasmulti.height / 2,
yVelocity: 0
};
const square2 = {
height: 75,
jumping: true,
width: 75,
x: 0,
xVelocity: 0,
y: canvasmulti.height / 2,
yVelocity: 0
};
//MOVEMENT:
const controller = {
left: false,
right: false,
up: false,
keyListener: function (event) {
let key_state = (event.type == "keydown") ? true : false;
switch (event.keyCode) {
case 37: // left arrow
controller.left = key_state;
break;
case 38: // up arrow
controller.up = key_state;
break;
case 39: // right arrow
controller.right = key_state;
break;
}
}
};
const controller2 = {
left: false,
right: false,
up: false,
keyListener: function (event) {
let key_state = (event.type == "keydown") ? true : false;
switch (event.keyCode) {
case 65: // left arrow
controller2.left = key_state;
break;
case 87: // up arrow
controller2.up = key_state;
break;
case 68: // right arrow
controller2.right = key_state;
break;
}
}
};
const loop = function () {
//controller one
if (controller.up && square.jumping == false) {
square.yVelocity -= 30;
square.jumping = true;}
if (controller.left) {
square.xVelocity -= 0.5;}
if (controller.right) {
square.xVelocity += 0.5;}
//controller two
if (controller2.up && square2.jumping == false) {
square2.yVelocity -= 30;
square2.jumping = true;}
if (controller2.left) {
square2.xVelocity -= 0.5;}
if (controller2.right) {
square2.xVelocity += 0.5;}
//controller one
square.yVelocity += 1.5;// gravity
square.x += square.xVelocity;
square.y += square.yVelocity;
square.xVelocity *= 0.9;// friction
square.yVelocity *= 0.9;// friction
//controller two
square2.yVelocity += 1.5;// gravity
square2.x += square2.xVelocity;
square2.y += square2.yVelocity;
square2.xVelocity *= 0.9;// friction
square2.yVelocity *= 0.9;// friction
// if square1 is falling below floor line
if (square.y > canvasmulti.height - 75) {
square.jumping = false;
square.y = canvasmulti.height - 75;
square.yVelocity = 0;
}
// if square2 is falling below floor line
if (square2.y > canvasmulti.height - 75) {
square2.jumping = false;
square2.y = canvasmulti.height - 75;
square2.yVelocity = 0;
}
// if square1 is going off the left of the screen
if (square.x < 0) {
square.x = 0;
} else if (square.x > canvasmulti.width - 75) {// if square goes past right boundary
square.x = canvasmulti.width - 75;
}
// if square2 is going off the left of the screen
if (square2.x < 0) {square2.x = 0;}
else if (square2.x > canvasmulti.width - 75) {// if square goes past right boundary
square2.x = canvasmulti.width - 75;
}
// Creates the backdrop for each frame
context.fillStyle = "#394129";
context.fillRect(0, 0, canvasmulti.width, canvasmulti.height); // x, y, width, height
// Creates and fills square1 for each frame
context.fillStyle = "#8DAA9D"; // hex for cube color
context.beginPath();
context.rect(square.x, square.y, square.width, square.height);
context.fill();
// Creates and fills square2 for each frame
context.fillStyle = "#781818"; // hex for cube color
context.beginPath();
context.rect(square2.x, square2.y, square2.width, square2.height);
context.fill();
// Collision detection of square1 with square2
if (square.x <= square2.x + square2.width &&
square.x >= square2.x + square2.width - square2.width/2 && square.y >= square2.y &&
square.y <= square2.y + square2.height)
{square.xVelocity = 0;}; // left square1 touching right square2
if (square.x + square.width >= square2.x && square.x + square.width <= square2.x + square2.width/2 &&
square.y >= square2.y &&
square.y <= square2.y + square2.height)
{square.xVelocity = 0;}; // right square1 touching left square2
if (square.x <= square2.x + square2.width &&
square.x >= square2.x && square.y + square.height >= square2.y )
{square.yVelocity = 0;}; // bottom left square1 touching top square2
if (square.x + square.width <= square2.x + square2.width &&
square.x + square.width >= square2.x && square.y + square.height >= square2.y )
{square.yVelocity = 0;}; // bottom right square1 touching top square2
// Collision detection of square2 with square1
// call update when the browser is ready to draw again
window.requestAnimationFrame(loop);
};
//square1
window.addEventListener("keydown", controller.keyListener)
window.addEventListener("keyup", controller.keyListener);
//square2
window.addEventListener("keydown", controller2.keyListener)
window.addEventListener("keyup", controller2.keyListener);
window.requestAnimationFrame(loop);
<canvas id="canvasmulti"></canvas>
The reason, I believe, is because you have already added the vector. So if you're going to make the velocity 0, you may want to set the position manually or subtract the velocity before clearing it.
for example:
if (square.x <= square2.x + square2.width &&
square.x >= square2.x + square2.width - square2.width/2 && square.y >= square2.y &&
square.y <= square2.y + square2.height)
{
// left square1 touching right square2
square.x = square2.x + square2.width; // <== set it to a position where they don't overlap
square.x -= square.xVelocity; // <== OR just subtract it
square.xVelocity = 0;
}
it gets a bit more complicated with the y vector, since you need to consider the gravity too, so setting the value may work better than adjusting it.

Moving character on canvas but not on the screen

I am making a 2d game in JavaScript but my character can only move around the length of the screen so I was wondering if there was a way to make the canvas move but my character stay on the middle of the screen could Simone please help me
here is my code for the test game
<html>
<body>
<canvas id="canvas">
</canvas>
</body>
</html>
<script>
var audio = new Audio('sounds/theServerRoom.mp3');
audio.play();
// Create the canvas
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function() {
bgReady = true;
};
bgImage.src = "images/gamemap.png";
//computer
var computerReady = false;
var computerImage = new Image();
computerImage.onload = function() {
computerReady = true;
};
computerImage.src = "images/computer1.png";
//hp box
var hpBoxReady = false;
var hpBoxImage = new Image();
hpBoxImage.onload = function() {
hpBoxReady = true;
};
hpBoxImage.src = "images/hpbox.png";
// player image
var playerReady = false;
var playerImage = new Image();
playerImage.onload = function() {
playerReady = true;
};
playerImage.src = "images/char.png";
// enemy image
var enemyReady = false;
var enemyImage = new Image();
enemyImage.onload = function() {
enemyReady = true;
};
enemyImage.src = "images/enemy_idle01.png";
var computer = {
wifi: true,
x: 399,
y: 200
}
// Game objects
var hpBox = {
restoreHealth: 34,
x: 300,
y: 300
}
var player = {
hackingSkill : 10,
stamina: 7,
health: 100,
sprintSpeed: 400,
weakSpeed: 150,
speed: 300 // movement in pixels per second
};
var enemy = {
speed: 250,
viewDistance: 40
};
var enemysCaught = 0;
// Handle keyboard controls
var keysDown = {};
addEventListener("keydown", function(e) {
keysDown[e.keyCode] = true;
}, false);
addEventListener("keyup", function(e) {
delete keysDown[e.keyCode];
}, false);
// Reset the game when the player catches a enemy
var reset = function() {
player.x = canvas.width / 2;
player.y = canvas.height / 2;
// Throw the enemy somewhere on the screen randomly
enemy.x = 32 + (Math.random() * (canvas.width - 64));
enemy.y = 32 + (Math.random() * (canvas.height - 64));
};
//w is 87
//a is 65
//s is 83
//d is 68
// Update game objects
var update = function(modifier) {
if (87 in keysDown) { // Player holding up
player.y -= player.speed * modifier;
}
if (83 in keysDown) { // Player holding down
player.y += player.speed * modifier;
}
if (65 in keysDown) { // Player holding left
player.x -= player.speed * modifier;
}
if (68 in keysDown) { // Player holding right
player.x += player.speed * modifier;
}
if (
player.x <= (0)) {
player.health -= 1;
console.log('health decreasing');
}
}
if (
player.y <= (0)) {
player.health -= 1;
console.log('health decreasing');
};
// Are they touching?
if (
player.x <= (enemy.x + 32) &&
enemy.x <= (player.x + 32) &&
player.y <= (enemy.y + 32) &&
enemy.y <= (player.y + 32)
) {
++enemysCaught;
reset();
}
// Draw everything
var render = function() {
if (bgReady) {
context.drawImage(bgImage, 0, 0);
}
if (computerReady) {
context.drawImage(computerImage, computer.x, computer.y);
}
if (hpBoxReady) {
context.drawImage(hpBoxImage, hpBox.x, hpBox.y);
}
if (playerReady) {
context.drawImage(playerImage, player.x, player.y);
}
if (enemyReady) {
context.drawImage(enemyImage, enemy.x, enemy.y);
}
// Score
};
function dieEvent() {
player.health = 100;
}
function updateHealth() {
context.fillStyle = "white";
context.textAlign = "left";
context.fillText("Health: " + player.health, 30, 32);
context.fillStyle="#FF0000";
context.fillRect(10,10,(player.health/100)*140,25);
context.stroke();
}
function updateHackerSkill(){
context.fillStyle = "green";
context.textAlign = "left";
context.fillText("Health: " + player.hackerSkill, 30, 32);
context.fillStyle="#FF0000";
context.fillRect(10,10,(player.hackerSkill/100)*1,45);
context.stroke();
}
function isNearComputer() {
if (player.y <= (computer.y + enemy.viewDistance + 23) &&
player.y >= (computer.y - enemy.viewDistance) &&
player.x <= (computer.x + enemy.viewDistance + 32) &&
player.x >= (computer.x - enemy.viewDistance)) {
console.log("near computer");
context.fillStyle = "black";
context.fillRect(0, 0, canvas.width, canvas.height);
context.stroke();
context.fillStyle = "green";
context.font = "24px Helvetica";
context.textAlign = "left";
context.textBaseline = "top";
context.fillText("Welcome to uOS v1.0 " , 20 ,10);
window.setTimeout(500);
context.fillText("user$> " , 20 ,35);
}
}
function isNearHPBox() {
if (
player.y <= (hpBox.y + enemy.viewDistance + 64) &&
player.y >= (hpBox.y - enemy.viewDistance - 64) &&
player.x <= (hpBox.x + enemy.viewDistance + 64) &&
player.x >= (hpBox.x - enemy.viewDistance - 64)) {
console.log("healing!");
if (player.health <= 100) {
hpBox.restoreHealth = player.health - 100;
player.health += hpBox.restoreHealth;
}
}
}
function moveEnemy() {
if (
player.y <= (enemy.y + enemy.viewDistance + 64) &&
player.y >= (enemy.y - enemy.viewDistance - 64) &&
player.x <= (enemy.x + enemy.viewDistance + 64) &&
player.x >= (enemy.x - enemy.viewDistance - 64)) {
console.log("seen on enemys Y");
var audio = new Audio('sounds/theWanderer_Scream.m4a');
audio.play();
if (player.x >= (enemy.x)) {
enemy.x -= enemy.speed;
}
if (player.x >= (enemy.x)) {
enemy.x -= enemy.speed;
}
}
}
function checkWallCollision() {
if (player.y <= 0) {
console.log("y")
player.y += 64;
}
if (player.x <= 0) {
console.log("x")
player.x += 64;
}
if (enemy.y <= 0) {
console.log("y")
enemy.y += 64;
}
if (enemy.x <= 0) {
console.log("x")
enemy.x += 64;
}
}
// function updateMouseCoords(){
// document.onmousemove = function(e){
// cursorX = e.pageX;
// cursorY = e.pageY;
// context.fillStyle = "green";
// context.font = "24px Helvetica";
// context.textAlign = "left";
// context.textBaseline = "top";
// context.fillText("x" + cursorX + "y" + cursorY , 20 ,10);
//
// }
// }
// function drawViewLine(){
// var cursorX;
// var cursorY;
// context.beginPath();
// context.moveTo(player.x,player.y);
// context.lineTo(cursorX,cursorY);
// context.stroke();
// console.log("drawing line")
// }
function reducedSpeed() {
player.speed = player.weakSpeed;
}
// The main game loop
var main = function() {
var now = Date.now();
var delta = now - then;
update(delta / 1000);
context.clearRect(0, 0, canvas.width, canvas.height);
render();
updateHealth();
moveEnemy();
if (player.health <= 20) {
reducedSpeed();
} else {
player.speed = 300;
}
if (player.health <= 0) {
dieEvent();
}
checkWallCollision();
isNearHPBox();
isNearComputer();
//updateMouseCoords();
//drawViewLine();
then = now;
// Request to do this again ASAP
requestAnimationFrame(main);
};
// Cross-browser support for requestAnimationFrame
var w = window;
requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame;
// Let's play this game!
var then = Date.now();
reset();
main();
</script>
<style>
body {
margin: 0;
padding: 0;
}
</style>
there are multiple ways to deal with this issue, here are the main 2 i can think of right now:
1.- Move the world instead of your character: what you do is that your character actually stays in place, and you move the rest of the world instead, this is mainly useful for runner games, where movement is constant or one-dimensional (only left-right or up-down but never both in the same level)
2.- I haven't tested this, but what if your canvas is actually the size of the level, but it is partially hidden by a parent DIV with a specific width and height? this way you can move your character around the canvas, and move the canvas around the div to keep the character centered.
These solutions may or may not translate properly to your specific game though, since you gave literally no details about how movement is being dealt with at the moment.

Javascript canvas game

I have a problem with my canvas game. I try to make it jump but I have some troubles with it. Everything is working fine but if I touch an object from the bottom, it throw me up to the object.
Problem may be with LastColison. Can someone help me ? GIF Image link.
function Block(x, y) {
var size = 80
GameObject.call(this, x*size, y*size, size)
this.img = document.getElementById("block")
this.class = "block"
}
// Dedi vlastnosti z GameObject
Block.prototype = Object.create(GameObject.prototype)
Block.prototype.draw = function() {
ctx.fillStyle = "green"
ctx.drawImage(this.img,this.x,this.y,this.size,this.size)
}
function Player(x, y) {
var size = 120
this.dx = Math.random() * 50 - 25
this.dy = Math.random() * 50 - 25
GameObject.call(this, x*size, y*size, size)
// this.player = document.getElementById("player")
this.img = [document.getElementById("player"),document.getElementById("ball_r"),document.getElementById("ball_l"),document.getElementById("ball_j")]
this.player = this.img[0]
this.class = "player"
}
// Dedi vlastnosti z GameObject
Player.prototype = Object.create(GameObject.prototype)
Player.prototype.move = function() {
var x = this.x
var y = this.y
//ak dam this.y = y -5 môžem pohnuť aj so stlačenou sipkou dole
this.player = this.img[0]
// Posun
if ( keys[37] )
{
if(level == 0){
x -= 4;
}
if (level == 1) {
x -= 8;
}
this.player = this.img[2];
this.y = y;
}
if ( keys[39])
{
if (level == 0) {
x += 4;
}
if (level == 1) {
x += 8;
}
this.player = this.img[1];
}
if ( keys[38] )
{ this.player = this.img[3], this.dy = -10; }
// ak nedam else if mozem ouzzivat naraz viac tlacidiel takze upravit potom
// Test novej pozicie
var collision = false
for (i in scene) {
var obj = scene[i]
if (obj.class == "cloud") { continue; }
if (obj.class == "ladder") { continue; }
if (obj.class == "touched") { continue; }
if (obj.class == "dirt") { this.x = x; this.y = y }
if (obj.class == "block") { this.x = x; this.y = y }
if (obj.class == "enemy") { this.x = x; this.y = y}
var test = x +30 >= obj.x + obj.size || x + this.size - 40<= obj.x /* kolide right*/|| y >= obj.y + obj.size /*kolizia up*/|| y + 40 + this.size <= obj.y /*kolizia bottom*/
if (!test) {
collision = true;
var touch = 0;
if (obj.class == "enemy") {
touch = 1;
if (touch == 1) {
health -= 20; console.log(health);
this.x = x - 250;
if (klik % 2 == 0){
var hit = new Audio('snd/Hit_Hurt15.wav')
hit.play()
}
}
if (health == 0) { health = 0; console.log("GAMEOVER");scene = []}
}
if (obj.class == "coin") {
score += 10; obj.class = "touched";
if (klik % 2 == 0) {
var hrahra = new Audio('snd/pickcoin.wav')
hrahra.play()
}
}
else { touch = 0; }
if (obj.class == "touched") {}
break;
}
}
if (score >= 200 && score <= 200) {
if (klik % 2 == 0) {
var levelup = new Audio('snd/Powerup9.wav')
levelup.loop = false;
levelup.play()
}
level = 1;
health = 100;
score += 1;
}
// Ladder
// if(collision){score += 1,scene.pop() }
// Posun bez kolizie
if (!collision) {
this.x = x
this.y = y + this.dy
this.dy += 0.3;
}
**else {
if (obj.class == this.LastColl) {
this.dy = 0;
this.y = obj.y -160
}
this.dy = 0;
this.LastColl = obj.class
}**
}
Player.prototype.draw = function() {
ctx.fillStyle = "blue"
ctx.beginPath()
ctx.drawImage(this.player,this.x, this.y, 110,160)
ctx.shadowColor = "rgba( 0, 0, 0, 0.3 )";
ctx.shadowOffsetX = -10;
ctx.shadowOffsetY = 0
ctx.shadowBlur = 3;
ctx.drawImage(this.player,this.x,this.y,110,160)
ctx.closePath()
ctx.fill()
}
I can't currently access the GIF you provided. But from what i can gather these lines are your problem:
if (!collision) {
this.x = x
this.y = y + this.dy
this.dy += 0.3;
}
**else {
if (obj.class == this.LastColl) {
this.dy = 0;
this.y = obj.y -160
}
This line - this.y = obj.y -160
Looks like you're telling it to move up -160 pixels on the canvas.
Does this answer your question?
Just a note too - I'd recommend using semicolons at the end of each statement. Don't sometimes use them and other times don't - it will cause problems for you and is bad practice :)
I don't know much about canvas right now, but I noticed that you don't have any semicolons to end your statements...
example:
var x = this.x;
etc.
Another thing that I noticed... The score checks for both greater than or equal to and less than or equal to 200...
if (score >= 200 && score <= 200)
{
...
}

Categories