In my code, I expected, a normal block hopper game, but the isCollide function doesn't seem to detect all the collisions, if the beginning cude doesn't hit you it only detects collisions when it hits you in the air.
var mouseX, mouseY;
canvas.addEventListener('mousemove', function(event) {
var rect = canvas.getBoundingClientRect();
mouseX = event.clientX - rect.left;
mouseY = event.clientY - rect.top;
});
var canvasWidth = 600;
var canvasHeight = 400;
var player;
var playerYPosition = 200;
var state = 0;
var fallSpeed = 0;
var interval = setInterval(updateCanvas, 20);
var isJumping = false;
var jumpSpeed = 0;
var block;
var pause;
var score = 0;
var lives = 3;
var special = false;
var message = "";
var messageX = canvasWidth / 2 - 50;
var messageY = canvasHeight / 2;
var width;
var height;
var speed;
var scoreLabel;
var livesLabel;
var lost = new Audio('../Lost_game.mp3');
var lostLife = new Audio('../Lost_Life.mp3');
var jump = new Audio('../jump.mp3');
function checkState() {
if (state === 1) {
pause.image.src = "../play.png";
clearInterval(interval);
state = 2;
}
else if (state === 2) {
gameCanvas.canvas.style.backgroundColor = "#87ceeb"
pause.image.src = "../Pause.png";
interval = setInterval(updateCanvas, 20);
state = 1;
}
else if (state === 3) {
pause.image.src = "../Pause.png";
message = ""; // Clear the message
width = randomNumber(10, 50);
height = randomNumber(50, 100);
speed = randomNumber(4, 6);
block.y = canvasHeight - height;
block.x = canvasWidth;
interval = setInterval(updateCanvas, 20);
state = 1;
lives = 3;
}
else if (state === 4) {
pause.image.src = "../Pause.png";
message = ""; // Clear the message
width = randomNumber(10, 50);
height = randomNumber(50, 100);
speed = randomNumber(4, 6);
block.y = canvasHeight - height;
block.x = canvasWidth;
interval = setInterval(updateCanvas, 20);
state = 1;
}
else if (state === 5) {
window.location.reload();
}
}
function startGame() {
gameCanvas.start();
ctx = gameCanvas.context;
lives = 3;
message = "";
state = 1;
player = new createPlayer(30, 30, 10);
block = new createBlock();
pause = new createImg("../play.png", (gameCanvas.canvas.width / 2 - 25), 0, 50, 50);
state = 2;
scoreLabel = new createScoreLabel(10, 30);
livesLabel = new createScoreLabel(490, 30);
clearInterval(interval);
var titlep1 = "Block"
var titlep2 = "Hopper"
var titlep1Width = ctx.measureText(titlep1).width;
var titlep2Width = ctx.measureText(titlep2).width;
ctx.font = "150px titleFont";
ctx.fillStyle = "white"
ctx.fillText(titlep1, canvasWidth / 2 - titlep1Width * 8.7, canvasHeight / 2 - 30)
ctx.fillText(titlep2, canvasWidth / 2 - titlep2Width * 6.5, canvasHeight / 2 + 70)
}
var gameCanvas = {
canvas: document.getElementById("canvas"),
start: function() {
this.canvas.width = canvasWidth;
this.canvas.height = canvasHeight;
this.context = this.canvas.getContext("2d");
}
}
function createPlayer(width, height, x) {
return {
width: width,
height: height,
x: x,
y: playerYPosition,
draw: function() {
ctx = gameCanvas.context;
ctx.fillStyle = "green";
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.strokeRect(this.x, this.y, this.width, this.height);
},
makeFall: function() {
if (!isJumping) {
this.y += fallSpeed;
fallSpeed += 0.3;
this.stopPlayer();
}
},
stopPlayer: function() {
var ground = canvasHeight - this.height;
if (this.y > ground) {
this.y = ground;
}
},
jump: function() {
if (isJumping) {
this.y -= jumpSpeed;
jumpSpeed += 0.3;
}
}
}
}
function createBlock() {
width = randomNumber(10, 50);
height = randomNumber(10, 200);
speed = randomNumber(2, 6);
return {
x: canvasWidth,
y: randomNumber(250, canvasHeight - height),
width: width,
height: height,
draw: function() {
if (special === false) {
ctx = gameCanvas.context;
ctx.fillStyle = "red";
ctx.strokeStyle = "black";
ctx.fillRect(this.x, this.y, width, height);
ctx.strokeRect(this.x, this.y, width, height);
}
else if (special === true) {
ctx.fillStyle = "gold";
ctx.strokeStyle = "black";
ctx.fillRect(this.x, this.y, width, height);
ctx.strokeRect(this.x, this.y, width, height);
}
},
attackPlayer: function() {
this.x -= speed;
this.returnToAttackPosition();
},
returnToAttackPosition: function() {
if (this.x < 0) {
var random = randomNumber(1, 30);
width = randomNumber(10, 50);
height = randomNumber(50, 200);
speed = randomNumber(4, 6);
if (Math.round(random)===1) {
speed = randomNumber(6, 10);
this.y = randomNumber(100, 250);
this.x = canvasWidth;
ctx.fillStyle="gold" ;
ctx.strokeStyle="black" ;
ctx.fillRect(this.x, this.y, width, height);
ctx.strokeRect(this.x, this.y, width, height);
special=true;
}
else if (Math.round(random) !==1) {
this.y = randomNumber(250, canvasHeight - height);
this.x=canvasWidth;
ctx.fillStyle="red" ;
ctx.strokeStyle="black" ; ctx.fillRect(this.x, this.y, width, height);
ctx.strokeRect(this.x, this.y, width, height);
special=false;
}
score++
}
}
};
}
function isCollide(rect1, rect2) {
if (rect1.x - speed <= rect2.x + rect2.width) {
if (rect1.x + rect1.width - speed >= rect2.x){
if (rect1.y <= rect2.y + rect2.height) {
if (rect1.y + rect1.height >= rect2.y) {
return true;
}
}
}
}
else {
return false;
}
}
function detectCollision() {
if (typeof block !== 'undefined' && typeof player !== 'undefined') {
if (isCollide(player, block) === true) {
if (special === false) {
if (lives <= 1) {
clearInterval(interval);
lost.play();
pause.image.src = "../play.png"
message = "You Died";
state = 6;
score = 0;
lives--;
setTimeout(startGame, 5000)
}
else {
clearInterval(interval);
lives--;
pause.image.src = "../play.png"
if (lives > 1 || lives === 0) {
message = lives.toString() + " Lives Left"; // Set the message
}
else if (lives === 1) {
message = lives.toString() + " Life Left"; // Set the message
}
lostLife.play();
state = 4;
}
}
else if (special === true) {
score = score + 10;;
special = null;
}
}
}
}
function createScoreLabel(x, y) {
this.score = 0;
this.x = x;
this.y = y;
this.draw = function() {
ctx = gameCanvas.context;
ctx.font = "25px Marker Felt";
ctx.fillStyle = "black";
ctx.fillText(this.text, this.x, this.y);
}
}
function createImg(imageUrl, x, y, width, height) {
this.canvas = document.getElementById('canvas');
this.ctx = this.canvas.getContext('2d');
this.image = new Image();
this.image.src = imageUrl;
this.x = x || 0;
this.y = y || 0;
this.width = width || 0;
this.height = height || 0;
this.draw = function() {
this.ctx.clearRect(this.x, this.y, this.width, this.height)
this.ctx.drawImage(this.image, this.x, this.y, this.width, this.height);
};
var _this = this;
this.image.onload = function() {
_this.draw();
};
}
function updateCanvas() {
detectCollision();
if (typeof gameCanvas.context !== 'undefined') {
ctx = gameCanvas.context;
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
player.makeFall();
player.draw();
player.jump();
block.draw();
block.attackPlayer();
pause.draw();
scoreLabel.text = "SCORE: " + score;
scoreLabel.draw();
livesLabel.text = "LIVES: " + lives;
livesLabel.draw();
ctx.font = "30px Arial";
ctx.fillStyle = "black";
var textWidth = ctx.measureText(message).width;
ctx.fillText(message, canvasWidth / 2 - textWidth / 2, canvasHeight / 2);
if (score >= 1000) {
clearInterval(interval);
ctx = gameCanvas.context;
ctx.clearRect(0, 0, canvasWidth, canvasHeight)
document.getElementById("canvas").style.backgroundColor = "#a4ad07"
ctx.beginPath();
ctx.arc(180, 120, 250, 0, 2 * Math.PI);
ctx.fillStyle = 'yellow';
ctx.fill();
ctx.closePath();
ctx.font = "100px Helvetica, Arial, sans-serif";
ctx.fillStyle = "#098f05";
ctx.fillText("YOU WON!", 45, 230);
pause.image.src ="../play.png"
pause.draw();
lives = 3;
score = 0;
state = 2;
}
}
}
function randomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
function resetJump() {
jumpSpeed = 0;
isJumping = false;
}
document.body.onkeydown = function(e) {
if (e.keyCode == 32) {
e.preventDefault();
}
}
document.body.onkeyup = function(e) {
if (e.keyCode == 32) {
if (state === 1) {
jump.play();
isJumping = true;
setTimeout(function() { resetJump(); }, 1000);
}
}
}
document.getElementById("canvas").addEventListener("click", function() {
var buttonX = pause.x;
var buttonY = pause.y;
var buttonWidth = pause.width;
var buttonHeight = pause.height;
if (mouseX >= buttonX && mouseX <= buttonX + buttonWidth && mouseY>= buttonY && mouseY <= buttonY + buttonHeight && state < 6) {
checkState();
}
else if (state === 1) {
jump.play();
isJumping = true;
setTimeout(function() { resetJump(); }, 1000);
}
});
I tried
function isCollide(a, b, speedX, speedY) {
return (
a.x + speedX < b.x + b.width &&
a.x + a.width + speedX > b.x &&
a.y + speedY < b.y + b.height &&
a.y + a.height + speedY > b.y
);
}
But it had the same problem. So I tried a different solution.
function isCollide(a, b) {
return (
Math.min(a.x, a.x - a.speedX) <= b.x + b.width && //In case the block is moving (it is).
Math.max(a.x + a.width, a.x + a.width - a.speedX) >= b.x &&
a.y <= b.y + b.height &&
a.y + a.height > b.y
);
}
Had the same problem, so eventually I tried this solution with SAT.
function isCollide(rect1, rect2) {
// Function to project a rectangle onto an axis and return the minimum and maximum values
function projectRectangle(rect, axis) {
var dotProduct = rect.x * axis.x + rect.y * axis.y;
var min = dotProduct;
var max = dotProduct + (rect.width * axis.x + rect.height * axis.y);
return { min: min, max: max };
}
// Function to check if two projected intervals overlap
function intervalsOverlap(interval1, interval2) {
return interval1.min <= interval2.max && interval2.min <= interval1.max;
}
// Calculate the axes to test
var axes = [
{ x: rect1.x - rect1.width, y: rect1.y }, // Axis perpendicular to rect1's left side
{ x: rect1.x, y: rect1.y - rect1.height }, // Axis perpendicular to rect1's top side
{ x: rect2.x - rect2.width, y: rect2.y }, // Axis perpendicular to rect2's left side
{ x: rect2.x, y: rect2.y - rect2.height } // Axis perpendicular to rect2's top side
];
// Check for overlap on each axis
for (var i = 0; i < axes.length; i++) {
var axis = axes[i];
var projection1 = projectRectangle(rect1, axis);
var projection2 = projectRectangle(rect2, axis);
if (!intervalsOverlap(projection1, projection2)) {
// No overlap found, rectangles are not colliding
return false;
}
}
// If we reach this point, there is overlap along all axes, indicating a collision
return true;
}
But it also didn't work.