I've been trying to figure out how I can add a play again button to refresh the page after a player has lost. This is a classic snake game. I followed a tutorial on it and but they never said anything about a Play Again button. Any help would be appreciated. Nothing too complicated though please, I'm still learning.
const ctx = canvas.getContext("2d");
class SnakePart {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
let speed = 7;
let tileCount = 20;
let tileSize = canvas.width / tileCount - 2;
let headX = 10;
let headY = 10;
const snakeParts = [];
let tailLength = 2;
let appleX = 5;
let appleY = 5;
let inputsXVelocity = 0;
let inputsYVelocity = 0;
let xVelocity = 0;
let yVelocity = 0;
let score = 0;
const gulpSound = new Audio("gulp.mp3");
//game loop
function drawGame() {
xVelocity = inputsXVelocity;
yVelocity = inputsYVelocity;
changeSnakePosition();
let result = isGameOver();
if (result) {
return;
}
clearScreen();
checkAppleCollision();
drawApple();
drawSnake();
drawScore();
if (score > 5) {
speed = 9;
}
if (score > 10) {
speed = 11;
}
setTimeout(drawGame, 1000 / speed);
}
function isGameOver() {
let gameOver = false;
if (yVelocity === 0 && xVelocity === 0) {
return false;
}
//walls
if (headX < 0) {
gameOver = true;
} else if (headX === tileCount) {
gameOver = true;
} else if (headY < 0) {
gameOver = true;
} else if (headY === tileCount) {
gameOver = true;
}
for (let i = 0; i < snakeParts.length; i++) {
let part = snakeParts[i];
if (part.x === headX && part.y === headY) {
gameOver = true;
break;
}
}
if (gameOver) {
ctx.fillStyle = "white";
ctx.font = "50px Verdana";
if (gameOver) {
ctx.fillStyle = "white";
ctx.font = "50px Verdana";
var gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop("0", " magenta");
gradient.addColorStop("0.5", "blue");
gradient.addColorStop("1.0", "red");
// Fill with gradient
ctx.fillStyle = gradient;
ctx.fillText("Game Over!", canvas.width / 6.5, canvas.height / 2);
}
ctx.fillText("Game Over!", canvas.width / 6.5, canvas.height / 2);
}
return gameOver;
}
function drawScore() {
ctx.fillStyle = "white";
ctx.font = "10px Verdana";
ctx.fillText("Score " + score, canvas.width - 50, 10);
}
function clearScreen() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
function drawSnake() {
ctx.fillStyle = "green";
for (let i = 0; i < snakeParts.length; i++) {
let part = snakeParts[i];
ctx.fillRect(part.x * tileCount, part.y * tileCount, tileSize, tileSize);
}
snakeParts.push(new SnakePart(headX, headY)); //put an item at the end of the list next to the head
while (snakeParts.length > tailLength) {
snakeParts.shift(); // remove the furthet item from the snake parts if have more than our tail size.
}
ctx.fillStyle = "orange";
ctx.fillRect(headX * tileCount, headY * tileCount, tileSize, tileSize);
}
function changeSnakePosition() {
headX = headX + xVelocity;
headY = headY + yVelocity;
}
function drawApple() {
ctx.fillStyle = "red";
ctx.fillRect(appleX * tileCount, appleY * tileCount, tileSize, tileSize);
}
function checkAppleCollision() {
if (appleX === headX && appleY == headY) {
appleX = Math.floor(Math.random() * tileCount);
appleY = Math.floor(Math.random() * tileCount);
tailLength++;
score++;
gulpSound.play();
}
}
document.body.addEventListener("keydown", keyDown);
function keyDown(event) {
//up
if (event.keyCode == 38 || event.keyCode == 87) {
//87 is w
if (inputsYVelocity == 1) return;
inputsYVelocity = -1;
inputsXVelocity = 0;
}
//down
if (event.keyCode == 40 || event.keyCode == 83) {
// 83 is s
if (inputsYVelocity == -1) return;
inputsYVelocity = 1;
inputsXVelocity = 0;
}
//left
if (event.keyCode == 37 || event.keyCode == 65) {
// 65 is a
if (inputsXVelocity == 1) return;
inputsYVelocity = 0;
inputsXVelocity = -1;
}
//right
if (event.keyCode == 39 || event.keyCode == 68) {
//68 is d
if (inputsXVelocity == -1) return;
inputsYVelocity = 0;
inputsXVelocity = 1;
}
}
drawGame();```
The easiest way to refresh the page would be just:
window.location.reload(false)
Source:
https://www.freecodecamp.org/news/location-reload-method-how-to-reload-a-page-in-javascript/
I would suggest that simply reloading the page is not a great way to implement a play again feature but I appreciate the creativity of it.
Related
So I was making a breakout game for class and finished it, so for fun, my teacher asked before I went off for Christmas break that I make the bricks change color, and yea I did that but it's WAY too fast. He says it's fine how it is- if it was slower, or if the colors changed every time a brick was hit. Can I get some help?
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ballRadius = 10;
x = canvas.width/2;
y = canvas.height-30;
dx = 2;
dy = -2;
const ballColor1 = "#0095DD";
const ballColor2 = "#ff0000";
var ballColor = ballColor1;
paddleHeight = 10;
paddleWidth = 75;
paddleX = (canvas.width-paddleWidth)/2;
rightPressed = false;
leftPressed = false;
const brickRowCount = 5;
const brickColumnCount = 3;
const brickWidth = 75;
const brickHeight = 20;
const brickPadding = 10;
const brickOffsetTop = 30;
const brickOffsetLeft = 30;
let score = 0;
let lives = 3;
const bricks = [];
for(let c=0; c<brickColumnCount; c++) {
bricks[c] = [];
for(let r=0; r<brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0, status: 1 };
}
}
function randBrick() {
return "#" + ((1 << 24) * Math.random() | 0).toString(16).padStart(6, "0")
}
console.log(randBrick());
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
document.addEventListener("mousemove", mouseMoveHandler, false);
document.getElementById("startAgain").addEventListener("click", function() {
document.location.reload();
clearInterval(interval);
});
document.getElementById("playAgain").addEventListener("click", function() {
document.location.reload();
clearInterval(interval);
});
function keyDownHandler(e) {
if(e.key == "Right" || e.key == "ArrowRight") {
rightPressed = true;
}
else if(e.key == "Left" || e.key == "ArrowLeft") {
leftPressed = true;
}
}
function keyUpHandler(e) {
if(e.key == "Right" || e.key == "ArrowRight") {
rightPressed = false;
}
else if(e.key == "Left" || e.key == "ArrowLeft") {
leftPressed = false;
}
}
function mouseMoveHandler(e) {
const relativeX = e.clientX - canvas.offsetLeft;
if(relativeX > 0 && relativeX < canvas.width) {
paddleX = relativeX - paddleWidth/2;
}
}
function collisionDetection() {
for(let c=0; c<brickColumnCount; c++) {
for(let r=0; r<brickRowCount; r++) {
const b = bricks[c][r];
if(b.status == 1) {
if(x > b.x && x < b.x+brickWidth && y > b.y && y < b.y+brickHeight) {
dy = -dy;
b.status = 0;
score++;
if(score == brickRowCount*brickColumnCount) {
document.getElementById('gameWin').style.display = 'block';
clearInterval(interval);
randBrick();
}
}
}
}
}
}
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = ballColor;
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height-paddleHeight, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function drawBricks() {
for(let c=0; c<brickColumnCount; c++) {
for(let r=0; r<brickRowCount; r++) {
if(bricks[c][r].status == 1) {
const brickX = (r*(brickWidth+brickPadding))+brickOffsetLeft;
const brickY = (c*(brickHeight+brickPadding))+brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = "#" + ((1 << 24) * Math.random() | 0).toString(16).padStart(6, "0");
ctx.fill();
ctx.closePath();
}
}
}
}
function drawScore() {
ctx.font = "16px Arial";
ctx.fillStyle = "#0095DD";
ctx.fillText("Score: "+score, 8, 20);
}
function drawLives() {
ctx.font = "16px Arial";
ctx.fillStyle = "#0095DD";
ctx.fillText("Lives: "+lives, canvas.width-65, 20);
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
drawBricks();
drawPaddle();
drawScore();
drawLives();
collisionDetection();
if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) {
dx = -dx;
if (ballColor == ballColor1) {
ballColor = ballColor2;
} else {
ballColor = ballColor1;
}
}
if(y + dy < ballRadius) {
dy = -dy;
if (ballColor == ballColor1) {
ballColor = ballColor2;
} else {
ballColor = ballColor1;
}
}
else if(y + dy > canvas.height-ballRadius) {
if(x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
}
else {
lives--;
if(!lives) {
document.getElementById('gameOver').style.display = 'block';
clearInterval(interval);
}
else {
x = canvas.width/2;
y = canvas.height-30;
dx = 3;
dy = -3;
paddleX = (canvas.width-paddleWidth)/2;
}
}
}
if(rightPressed && paddleX < canvas.width-paddleWidth) {
paddleX += 7;
}
else if(leftPressed && paddleX > 0) {
paddleX -= 7;
}
x += dx;
y += dy;
requestAnimationFrame(draw);
}
draw();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Breakout</title>
<style>
* {
padding: 0;
margin: 0;
}
canvas {
background: #eee;
display: block;
margin: 0 auto;
border: 5px solid red;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="480" height="320"></canvas>
<div id="gameOver" style="display: none; position: absolute; top: 50px; left: 50px; width: 200px;">
<h1>Game Over!</h1>
<button id="startAgain">Try Again?</button>
</div>
<div id="gameWin" style="display: none; position: absolute; top: 50px; left: 50px; width: 200px;">
<h1>You Win!</h1>
<button id="playAgain">Play Again?</button>
</div>
<script src=breakoutCode.js></script>
</body>
</html>
You're changing the color on every block draw. The Math.random() is getting called on every draw to the canvas thus that means it's changing every block's color on each draw.
Possible solutions:
Save the colors in an array + a the time you want to change them
Save the colors in an array and change them randomly
Don't re-draw the blocks on every re-draw (better for performance)
Here's one possible solution that stores the colors in an array and changes them randomly:
let blkColors = []
function drawBricks() {
for(let c=0; c<brickColumnCount; c++) {
for(let r=0; r<brickRowCount; r++) {
if(bricks[c][r].status == 1) {
const brickX = (r*(brickWidth+brickPadding))+brickOffsetLeft;
const brickY = (c*(brickHeight+brickPadding))+brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
if (!blkColors[c] || !blkColors[c][r] || Math.random() > 0.97) { // new color
if (!blkColors[c])
blkColors[c] = [];
ctx.fillStyle = blkColors[c][r] = "#" + ((1 << 24) * Math.random() | 0).toString(16).padStart(6, "0");
} else {
ctx.fillStyle = blkColors[c][r];
}
ctx.fill();
ctx.closePath();
}
}
}
}
On JSFiddle
I am trying to code a Pause/Play button for my breakout-clone because I have some text and links planned for the site on which the game will be, so you can pause the game while reading the text or interacting with links. My code is written in JS and mostly consists of "function" and "var". If the CSS or the HTML file is needed for an answer then please contact me. This is my first time asking anything on stackoverflow and I would be glad for any help.
var canvas = document.getElementById("TheCanvas");
var ctx = canvas.getContext("2d");
var x = canvas.width/2;
var y = canvas.height-50;
var dx = 2
var dy = -2;
var ballRadius = 10;
var paddleHeight = 15;
var paddleWidth = 80;
var paddleX = (canvas.width-paddleWidth) / 2;
var rightPressed = false;
var leftPressed = false;
var brickRowCount = 6;
var brickColumnCount = 4;
var brickWidth = 90;
var brickHeight = 20;
var brickPadding = 10;
var brickOffsetTop = 30;
var brickOffsetLeft = 15;
var score = 0;
var lives = 99;
var bricks = [];
for(var c=0; c<brickColumnCount; c++) {
bricks[c] = [];
for(var r=0; r<brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0,status: 1 };
}
}
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
document.addEventListener("mousemove", mouseMoveHandler, false);
function mouseMoveHandler(e) {
var relativeX = e.clientX - canvas.offsetLeft;
if(relativeX > 0 && relativeX < canvas.width) {
paddleX = relativeX - paddleWidth/2;
}
}
function keyDownHandler(e) {
if(e.key == "Right" || e.key == "ArrowRight") {
rightPressed = true;
}
else if(e.key === "Left" || e.key == "ArrowLeft") {
leftPressed = true;
}
}
function keyUpHandler(e) {
if(e.key === "Right" || e.key === "ArrowRight") {
rightPressed = false;
}
else if(e.key === "Left" || e.key === "ArrowLeft") {
leftPressed = false;
}
}
function collisionDetection() {
for(var c=0; c<brickColumnCount; c++) {
for(var r=0; r<brickRowCount; r++) {
var b = bricks[c][r];
if(b.status == 1) {
if(x > b.x && x < b.x+brickWidth && y > b.y && y < b.y+brickHeight) {
dy = -dy;
b.status = 0;
score++;
if(score == brickRowCount*brickColumnCount) {
alert("YOU WIN, WELL DONE!");
document.location.reload();
clearInterval(interval); // <- This is needed for the browser to end the game \\
}
}
}
}
}
}
function drawScore() {
ctx.font = "16px OCR A";
ctx.fillStyle = "#00ffd9"
ctx.fillText("Score: "+score, 8, 20);
}
function drawLives() {
ctx.font = "16px OCR A"
ctx.fillStyle = "00ffd9"
ctx.fillText("Lives: "+lives, canvas.width-85, 20);
}
function drawBall() {
// drawing code \\
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.lineWidth=3
ctx.strokeStyle = "#00ff00";
ctx.stroke();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height-15, paddleWidth, paddleHeight);
ctx.lineWidth=2
ctx.fillStyle = "#ff0000";
ctx.fill();
ctx.closePath();
}
function drawBricks() {
for(var c=0; c<brickColumnCount; c++) {
for(var r=0; r<brickRowCount; r++) {
if(bricks[c][r].status == 1) {
var brickX = (c*(brickWidth+brickPadding))+brickOffsetLeft;
var brickY = (r*(brickHeight+brickPadding))+brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.strokeStyle = "#33FFFF";
ctx.stroke();
ctx.closePath();
}
}
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBricks();
drawBall();
drawPaddle();
drawScore();
drawLives();
collisionDetection();
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if (y + dy < ballRadius) {
dy = -dy;
}
else if (y + dy > canvas.height - ballRadius) {
if (x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
}
else {
lives--;
if(!lives) {
alert("Game Over! Press 'Ok' to reload!");
document.location.reload();
clearInterval(interval); // Is needed for Browser to end the game \\
}
else {
x = canvas.width/2;
y = canvas.height-30
dx = 2;
dy = -2;
paddleX = (canvas.width-paddleWidth)/2;
}
}
}
if (rightPressed && paddleX < canvas.width - paddleWidth) {
paddleX += 3;
} else if (leftPressed && paddleX > 0) {
paddleX -= 3;
}
x += dx;
y += dy;
}
var interval = setInterval(draw, 9);
var checkbox = document.getElementById('darkmode');
function darkmode() {
if (checkbox.checked == true){
document.documentElement.style.setProperty('--primary-background', 'black');
document.documentElement.style.setProperty('--primary-color', 'white');
} else {
document.documentElement.style.setProperty('--primary-background', 'white');
document.documentElement.style.setProperty('--primary-color', 'black');
}
}
checkbox.addEventListener('click', darkmode);
darkmode();
I was making a snake game. I have a basic game but I wanted to move the snake more smoothly and specially I wanted to turn the direction of the snake more smoothly.
The problem until now is that I have been removing the tail of the snake and adding it to the head for the movement part.
Moving the snake smoothly is rather easy by reducing the interval for calling the draw function and incrementing the x and y values by small amounts but what I am having problem is with turning the snake smoothly,it is exactly where I am lost/confused as to how to do it like done here (only the turning of snake part and not the changing of head when snake gets blocked)
can anyone help out please? here is my code:
let cvs = "";
let ctx = "";
let size = [];
var direction = 'b';
var speed = 200;
//moving lines on arrow click
window.addEventListener("keydown", moveSomething, false);
function moveSomething(e) {
switch (e.keyCode) {
case 37:
//left
if (direction != 'r') {
direction = 'l';
}
break;
case 38:
//up
if (direction != 'b') {
direction = 't';
}
break;
case 39:
//right
if (direction != 'l') {
direction = 'r';
}
break;
case 40:
//bottom
if (direction != 't') {
direction = 'b';
}
break;
}
}
let food = {
x: Math.floor(Math.random() * 220),
y: Math.floor(Math.random() * 400),
s: 20,
draw: function() {
//this if block rounds off the x and y values
if ((this.x % 20) != 0 || (this.y % 20) != 0) {
if ((this.x % 20) != 0) {
let e = this.x % 20;
e = 20 - e;
this.x = this.x + e;
}
if ((this.y % 20) != 0) {
let e = this.y % 20;
e = 20 - e;
this.y = this.y + e;
}
};
ctx.fillStyle = "red";
ctx.fillRect(this.x, this.y, this.s, this.s);
},
newfood: function() {
this.x = Math.floor(Math.random() * 220);
this.y = Math.floor(Math.random() * 400);
this.draw();
}
}
const snake = {
s: 20,
draw: function(x, y) {
ctx.fillStyle = "green";
ctx.fillRect(this.s * x, this.s * y, this.s, this.s);
ctx.strokeStyle = "black";
ctx.strokeRect(this.s * x, this.s * y, this.s, this.s);
},
snakeInit: function() {
for (let i = 8; i >= 4; i--) {
size.push({
x: i,
y: 6
})
}
},
callDraw: function() {
for (let i = 0; i < size.length; i++) {
this.draw(size[i].x, size[i].y);
}
},
move: function() {
var snakeX = size[0].x;
var snakeY = size[0].y;
if (direction == 'r') {
snakeX++;
} else if (direction == 'l') {
snakeX--;
} else if (direction == 't') {
snakeY--;
} else if (direction == 'b') {
snakeY++;
}
//console.log("Inside move1", speed);
if (snakeX == -1 || snakeX == cvs.width / this.s || snakeY == -1 || snakeY == Math.floor(cvs.height / this.s) || this.checkSelfCollision(snakeX, snakeY, size)) {
ctx.clearRect(0, 0, cvs.width, cvs.height);
gameloop = clearInterval(gameloop);
return;
} else {
ctx.clearRect(0, 0, cvs.width, cvs.height);
food.draw();
}
if (snakeX * 20 == food.x && snakeY * 20 == food.y) {
var tail = {
x: snakeX,
y: snakeY
};
food.eaten();
speed += 200;
food.newfood();
} else {
var tail = size.pop();
tail.x = snakeX;
tail.y = snakeY;
}
size.unshift(tail);
for (let i = 0; i < size.length; i++) {
this.draw(size[i].x, size[i].y);
}
},
checkSelfCollision: function(x, y, arr) {
for (let i = 0; i < arr.length; i++) {
if (arr[i].x == x && arr[i].y == y) {
return true;
}
}
return false;
}
}
function loop() {
const cvsL = document.getElementById("snakes");
const ctxL = cvsL.getContext('2d');
cvs = cvsL;
ctx = ctxL;
snake.snakeInit();
snake.callDraw();
food.draw();
gameloop = setInterval(function() {
snake.move();
}, speed);
}
<body onload="loop();">
<canvas id="snakes" width=240px height=420px style="border: 1px solid black;display: block;"></canvas>
</body>
I'm trying to make a platforming game, and I've been working on the collision for the past 2 weeks. The collision detection is working, but the collision itself (as in, keeping the player out of the tile) is not, no matter what I try. I've tried looking up how to do this, but all I'm finding is how to do the detection part, which I already have done. What do I do after I detect collision?
It was written from scratch, and the player is rectangular, and so are the tiles.
Here's the basic code:
var Player = function(hue, x, y, xSize, ySize, health) {
this.hue = hue;
this.position = new PVector(x, y);
this.originalPosition = new PVector(x, y);
//this.previousPosition = new PVector(x, y);
//this.ppp = new PVector(x, y);
//this.virtualPosition = new PVector(x, y);
//this.predictedPosition = new PVector(x, y);
this.velocity = new PVector(0, 0);
//this.predictedVelocity = new PVector(0, 0);
this.acceleration = new PVector(0, 0);
}
/*Player.prototype.testCollision = function(tile) {
if (this.predictedPosition.y < tile.position.y + tile.size.y && this.predictedPosition.y + this.size.y > tile.size.y && this.predictedPosition.x < tile.position.x + tile.size.x && this.predictedPosition.x + tile.size.x > tile.position.x) {
return false;
} else {
return true;
}
};*/
Player.prototype.ifColliding = function(tile) {
if (this.position.x < tile.position.x + tile.size.x && this.position.x + tile.size.x > tile.position.x) {
/*if (this.position.x + this.size.x > tile.position.x) {
this.position.set(tile.position.x - this.size.x, this.position.y);
} else if (this.position.x < tile.position.x + tile.size.x) {
this.position.set(tile.position.x + tile.size.x, this.position.y);
}*/
this.velocity.set(0, this.velocity.y);
//this.acceleration.set(0, this.acceleration.y);
/*if (this.ppp.x < tile.position.x + tile.size.x && this.ppp.x + tile.size.x > tile.position.x) {
if (this.ppp.x + this.size.x > tile.position.x) {
this.position.set(tile.position.x - this.size.x, this.position.y);
} else if (this.ppp.x < tile.position.x + tile.size.x) {
this.position.set(tile.position.x + tile.size.x, this.position.y);
}
} else if (this.previousPosition.x < tile.position.x + tile.size.x && this.previousPosition.x + tile.size.x > tile.position.x) {
this.position.set(this.ppp.x, this.position.y);
} else {
this.position.set(this.previousPosition.x, this.position.y);
}*/
}
if (this.position.y < tile.position.y + tile.size.y && this.position.y + this.size.y > tile.size.y) {
this.velocity.set(this.velocity.x, 0);
this.acceleration.set(this.acceleration.x, 0);
this.yColliding = true;
/*if (this.position.y + this.size.y > tile.position.y) {
this.position.set(this.position.x, tile.position.y - this.size.y);
rect(0, 20, 0, 0);
} else if (this.position.y < tile.position.y + tile.size.y) {
this.position.set(this.position.x, tile.position.y + tile.size.y);
rect(20, 20, 0, 0);
}*/
}
}
Player.prototype.update = function(tiles) {
//this.ppp.set(this.previousPosition.x, this.previousPosition.y);
//this.previousPosition.set(this.position.x, this.position.y);
this.velocity.add(this.acceleration);
/*this.predictedVelocity.set(this.velocity.x, this.velocity.y);
this.predictedVelocity.add(this.acceleration);
this.virtualPosition.set(this.position.x, this.position.y);
this.virtualPosition.add(this.velocity);
this.predictedPosition.set(this.virtualPosition.x, this.virtualPosition.y);
this.predictedPosition.add(this.predictedVelocity);
var collDcted = false;
for (var i = 0; i < tiles.length; i++) {
if (this.testCollision(tiles[i], true) === false) {
collDcted = false;
}
}*/
//if (collDcted) {
this.position.add(this.velocity);
//}
}
The commented out code is failed attempts. The non-commented code is the closest I could get it to working.
This is a sample collision I made:
<!DOCTYPE html>
<html>
<body>
<p id="Health">Health</p>
<canvas id="gameCanvas" width="600" height="480" style = "border:1px solid gray"></canvas>
<script>
// Adding keyboard evt listener
document.addEventListener("keydown", keyPressed);
document.addEventListener("keyup", keyReleased);
//defining canvas
var canvas;
var canvasContext;
//defining Player variables
var PLAYER_X = 100;
var PLAYER_Y = 100;
var PLAYER_WIDTH = 20;
var PLAYER_HEIGHT = 20;
var PLAYER_HEALTH = 100;
//defining keypress codes
var KEY_LEFT = 37;
var KEY_RIGHT = 39;
var KEY_UP = 38;
var KEY_DOWN = 40;
//variables used to test movement
var keyHeld_Up = false;
var keyHeld_Down = false;
var keyHeld_Left = false;
var keyHeld_Right = false;
//Keypress?
function keyPressed(evt) {
if(evt.keyCode == KEY_UP) {
keyHeld_Up = true;
}
if(evt.keyCode == KEY_DOWN) {
keyHeld_Down = true;
}
if(evt.keyCode == KEY_LEFT) {
keyHeld_Left = true;
}
if(evt.keyCode == KEY_RIGHT) {
keyHeld_Right = true;
}
//prevents page from scrolling when arrow keys are pressed
evt.preventDefault();
}
//Key Released?
function keyReleased(evt) {
if(evt.keyCode == KEY_UP) {
keyHeld_Up = false;
}
if(evt.keyCode == KEY_DOWN) {
keyHeld_Down = false;
}
if(evt.keyCode == KEY_LEFT) {
keyHeld_Left = false;
}
if(evt.keyCode == KEY_RIGHT) {
keyHeld_Right = false;
}
}
//Initialize Canvas and Game Loop
window.onload = function() {
console.log("Is this thing on?");
canvas = document.getElementById('gameCanvas');
canvasContext = canvas.getContext('2d');
var framesPerSecond = 30;
setInterval(function() {
drawObjects();
movePlayer();
damageTest();
}, 1000/framesPerSecond);
}
// Drawing function
function colorRect(x,y, width,height, color, health) {
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.color = color;
this.health = health;
this.update = function() {
this.draw();
}
this.draw = function() {
canvasContext.beginPath();
canvasContext.rect(this.x, this.y, this.width, this.height);
canvasContext.fillStyle = this.color;
canvasContext.fill();
canvasContext.closePath();
}
};
// Creating Objects
var Screen = new colorRect( 0, 0, 600, 480, 'black', 0);
var Player = new colorRect( PLAYER_X, PLAYER_Y, PLAYER_WIDTH, PLAYER_HEIGHT, 'red', PLAYER_HEALTH);
var Box = new colorRect( 200, 200, 30, 30, 'green', 0);
var Spike = new colorRect( 300, 300, 25, 25, 'white', 0);
// Drawing Objects
function drawObjects() {
Screen.update();
Spike.update();
Player.update();
Box.update();
}
//Collision Test
function collides( a, b ) {
return a.x < b.x + b.width &&
a.x + a.width > b.x &&
a.y < b.y + b.height &&
a.y + a.height > b.y;
}
//Movement based on keypress events
function movePlayer() {
if(collides( Player, Box ) === false) {
if(keyHeld_Up) {
Player.y -= 2;
}
if(keyHeld_Down) {
Player.y += 2;
}
if(keyHeld_Left) {
Player.x -= 2;
}
if(keyHeld_Right) {
Player.x += 2;
}
}
}
//Testing Collision for damage
function damageTest() {
if(collides( Player, Spike ) === true) {
Player.health -= 1;
}
//Displaying Health in <body>
document.getElementById("Health").innerHTML = "Health: " + Player.health;
}
</script>
</body>
</html>
The code I made stops the player in its tracks completely when hitting the box, but you could create individual collision circumstances for when objects collide on each side of another object, and use those to detect collision.
I hope this helped! If you have any questions regarding this code, just ask! (To run code snippet you might want to go full screen and click inside canvas)
I am new to Javascript. I am trying to make a canvas game similar to Snake, but without the fruits.
The game is over if the player crosses his own path. The following is my code. Do you know how can I determine when the red rectangle crosses its own path and use the game over function?
Thank you!
var player;
var touch = 0;
function startGame() {
myGameArea.start();
player = new component(30, 30, "red", 270, 270);
}
var myGameArea = {
canvas: document.createElement("canvas"),
start: function() {
this.canvas.width = 600;
this.canvas.height = 600;
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;
})
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y) {
this.gamearea = myGameArea;
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;
}
}
function updateGameArea() {
/*myGameArea.clear();*/
player.speedX = 0;
player.speedY = 0;
if (myGameArea.key == 37) {
player.speedX = -10;
}
if (myGameArea.key == 39) {
player.speedX = 10;
}
if (myGameArea.key == 38) {
player.speedY = -10;
}
if (myGameArea.key == 40) {
player.speedY = 10;
}
if (player.x <= 0 || player.x >= 570 || player.y <= 0 || player.y >= 570) { //When the player goes out of the canvas
gameOver();
}
player.newPos();
player.update();
}
function gameOver() {
var r = confirm("GAME OVER. Restart?");
if (r == true) {
window.location.reload();
} else {
window.open("https://www.google.ca");
}
}
canvas {
padding-left: 0;
padding-right: 0;
margin-left: auto;
margin-right: auto;
display: block;
background-color: #000;
}
<body onload="startGame()">
</body>
Array as a Queue.
The way this game is traditionally done is using a special array type called a queue. Like a real queue you have items added at one end and removed at the other, or first in first out.
Javascript does not have a special array type for a queue but it has all the functions needed to implement a queue.
Push and shift
Array.push(item); // pushes an item onto the top of the array
Array.shift(); // removes an item from the start of the queue
So for the snake game imagine the head as the end of the queue. Every time it moves one forward you place another item on the queue. At the other end you remove an item if the length of the array is longer than the snake length;
var snake = [];
var snakeLength = 10;
function snakeMove(x,y){ // position to move head
checkForHit(x,y); // see below
snake.push({x:x,y:y}); // put another headpiece on the queue
if(snake.length > snakeLength){ // is the length longer than it should be
snake.shift(); // remove a tail item
}
}
To draw the snake you just iterate each part drawing it at the X,y position
To test if the snake has run its self over use the following function
function checkForHit(x,y){
for(var i = 0; i < snake.length; i++){
if(snake[i].x === x && snake[i].y === y){
// Snake has hit its self
}
}
}
When the snake eats something it traditionally grows in length. this is easily done by simply increasing the length variable. snakeLength += 1 makes the queue longer.
And a demo as i have not played the game in so long why not.
"use strict";
var score = 0;
var canvas = document.createElement("canvas");
var scoreE = document.createElement("div");
scoreE.style.color = "white";
scoreE.style.font = "16px arial";
scoreE.style.position = "absolute";
scoreE.style.top = "10px";
scoreE.style.left = "10px";
scoreE.style.width = "600px";
scoreE.style.textAlign = "center";
scoreE.textContent = "Click canvas area to get focus";
canvas.width = 600;
canvas.height = 200;
var ctx = this.canvas.getContext("2d");
document.body.appendChild(canvas);
document.body.appendChild(scoreE);
var lastKeyDown = 0;
window.addEventListener('keydown', function(e) {
lastKeyDown = e.keyCode;
e.preventDefault()
})
var snakePartSize = 8;
var playWidth = canvas.width /snakePartSize;
var playHeight = canvas.height /snakePartSize;
var snake = [];
var snakeLength = 10;
var snakePosX = 0;
var snakePosY = 0;
var snakeDirX = 0;
var snakeDirY = 0;
var snakeSpeed = 16; // number of frame between moves
var gameOver = true;
var instDrawn = false;
var food = [];
var foodFreq = 60;
var yum = 0;
var yumCol = ["red","orange","yellow"];
function startSnake(){
ctx.fillStyle = "black";
ctx.fillRect(0,0,canvas.width,canvas.height);
snakePosX = Math.floor(playWidth / 2);
snakePosY = Math.floor(playHeight / 2);
snakeDirX = 0;
snakeDirY = 0;
snakeLength = 10;
snake = [];
snakeSpeed = 16;
move(snakePosX,snakePosY); // set first pos
food = [];
score = 0;
}
function testHit(x,y){
if(x < 0 || y < 0 || y >= playHeight || x >= playWidth ){
return true;
}
for(var i = 0; i < snake.length; i ++){
if(snake[i].x === x && snake[i].y === y){
return true;
}
}
}
function testFood(x,y){
for(var i = 0; i < food.length; i ++){
if(food[i].x === x && food[i].y === y){
food.splice(i,1);
i --;
yum = 4;
score += 100;
snakeLength += 1;
if(snakeLength % 4 === 0){
snakeSpeed -= snakeSpeed > 1 ? 1:0;
}
}
}
}
function addFood(){
var x = Math.floor(Math.random() * playWidth );
var y = Math.floor(Math.random() * playHeight );
if(!testHit(x,y)){
food.push({x:x,y:y});
drawFood();
}
}
function move(x,y){
if(testHit(x,y)){
gameOver = true;
return;
}
testFood(x,y);
snake.push({x : x, y : y});
drawSnakeHead();
if(snake.length > snakeLength){
drawSnakeTail();
snake.shift();
}
}
function drawYum(){
for(var i = 0; i < snake.length; i ++){
ctx.fillStyle = yumCol[yum];
ctx.fillRect(snake[i].x*snakePartSize, snake[i].y*snakePartSize, snakePartSize, snakePartSize);
}
}
function drawFood(){
var f = food[food.length-1];
ctx.fillStyle = "green";
ctx.fillRect(f.x*snakePartSize, f.y*snakePartSize, snakePartSize, snakePartSize);
}
function drawSnakeHead(){
var head = snake[snake.length-1];
ctx.fillStyle = "red";
ctx.fillRect(head.x*snakePartSize, head.y*snakePartSize, snakePartSize, snakePartSize);
}
function drawSnakeTail(){
var head = snake[0];
ctx.fillStyle = "black";
ctx.fillRect(head.x*snakePartSize, head.y*snakePartSize, snakePartSize, snakePartSize);
}
var counter = 0;
function update(){
counter += 1;
if(!gameOver){
if(snakeDirX === 0){
if(lastKeyDown === 37){ // left
snakeDirX = -1;
snakeDirY = 0;
}
if(lastKeyDown === 39){ // right
snakeDirX = 1;
snakeDirY = 0;
}
}
if(snakeDirY === 0){
if(lastKeyDown === 38){ // up
snakeDirY = -1;
snakeDirX = 0;
}
if(lastKeyDown === 40){ // down
snakeDirY = 1;
snakeDirX = 0;
}
}
lastKeyDown = 0;
if(counter % foodFreq ===0){
addFood();
}
if(counter % snakeSpeed === 0){
snakePosX += snakeDirX;
snakePosY += snakeDirY;
score += 1;
move(snakePosX ,snakePosY);
}
if((counter % 2 === 0) && yum > 0){
yum -= 1;
drawYum();
}
scoreE.textContent = "Score : "+ score;
}
if(gameOver){
if(!instDrawn){
instDrawn = true;
ctx.fillStyle = "white";
ctx.font = "32px arial";
ctx.textAlign = "center";
ctx.fillText("GAME OVER",canvas.width /2, canvas.height /2);
ctx.font = "16px arial";
ctx.fillText("Press a direction key to start.",canvas.width /2, canvas.height /2+32);
}
if(lastKeyDown >= 37 && lastKeyDown <= 40){
gameOver = false;
instDrawn = false;
counter = -1;
startSnake();
}
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
startSnake();