Snake collides with itself if user presses arrow keys too fast - javascript

Iv'e finished my snake game but there's one more bug I can't fix. If the snake is going left for example the user can hit the up key then the right key quick enough so the snake never has a chance to move up 1 unit and resulting in the snake head turning in on itself. Any ideas how I could fix this? My brains telling me I need to have the snake move up 1 unit first before turning right, I just don't know what that looks like
//declare global variables
const canvas = document.querySelector('#canvas');
const score = document.querySelector('.score');
const gameOver = document.querySelector('.game-over');
//set canvas context
const ctx = canvas.getContext('2d');
//put canvas dimensions into variables
const cvsW = canvas.width;
const cvsH = canvas.height;
//load audio files
let eat = new Audio();
let dead = new Audio();
eat.src = 'audio/eat.mp3';
dead.src = 'audio/dead.mp3';
//create snake unit
const unit = 16;
//create points variable
let points = 0;
//create snake and set starting position
let snake = [{
x : cvsW/2,
y : cvsH/2
}]
//create food object and set its position somewhere on board
let food = {
//Math.floor(Math.random()*cvsW + 1)---number from 1 to 784
//Math.floor(Math.random()*cvsW/unit + 1)---number from 1 to 79
//Math.floor(Math.random()*cvsW/unit + 1)*unit---number from 1 to 784(but it's a multiple of unit)
//Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit---same as above but -1 keeps food inside canvas
x : Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit-unit/2,
y : Math.floor(Math.random()*(cvsH/unit - 1)+1)*unit-unit/2
}
//create a variable to store the direction of the snake
let direction;
//add event to read users input then change direction
document.addEventListener('keydown', (e) => {
if(e.keyCode == 37 && direction != 'right') direction = 'left';
else if (e.keyCode == 38 && direction != 'down') direction = 'up';
else if (e.keyCode == 39 && direction != 'left') direction = 'right';
else if (e.keyCode == 40 && direction != 'up') direction = 'down';
})
function draw() {
//clear canvas and redraw snake
ctx.clearRect(0, 0, cvsW, cvsH);
for(let i = 0; i < snake.length; i++) {
ctx.fillStyle = 'limegreen';
ctx.fillRect(snake[i].x-unit/2, snake[i].y-unit/2, unit, unit);
}
//draw food
ctx.fillStyle = 'red';
ctx.fillRect(food.x-unit/2, food.y-unit/2, unit, unit);
//grab heads position
let headX = snake[0].x;
let headY = snake[0].y;
//move snake in chosen direction
if(direction == 'left') headX -= unit;
else if(direction == 'right') headX += unit;
else if(direction == 'up') headY -= unit;
else if(direction == 'down') headY += unit;
//create new snake unit
let newHead = {x : headX, y :headY}
//check to see if snake has hit a wall or itself
if(headX < 0 || headX > cvsW || headY < 0 || headY > cvsH || collision(headX, headY)) {
dead.play();
clearInterval(runGame);
canvas.style.animationName = 'animate';
setTimeout(function() {gameOver.style.opacity = '1';}, 150);
}
//check to see if snakes eaten food
if(headX === food.x && headY === food.y) {
eat.play();
//increase score
points++;
score.innerText = points;
//get new food unit
getFood();
//create 3 new units
for(let i = 3; i > 0; i--) {
//add those units -without this code snake will not grow
snake.unshift(newHead);
}
} else {
//remove tail -without this code snake will keep growing
snake.pop();
}
//add new head position -without this code snake will not move
snake.unshift(newHead);
}
let runGame = setInterval(draw, 65);
function collision(x, y) {
for(let i = 1; i < snake.length; i++) {
if(x == snake[i].x && y == snake[i].y) return true;
}
return false;
}
function getFood() {
food = {
x : Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit-unit/2,
y : Math.floor(Math.random()*(cvsH/unit - 1)+1)*unit-unit/2
}
//loop through snake to see if food generates inside snake
for(let i = 0; i < snake.length; i++) {
//if so call the function again
if(food.x == snake[i].x && food.y == snake[i].y) return getFood();
}
//else return new random point
return food;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Snake</title>
<link href="https://fonts.googleapis.com/css?family=Nova+Square" rel="stylesheet">
<style>
body {
background-color: #333;
}
.game-over {
z-index: 1;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
width: 784px;
height: 528px;
margin: auto;
color: red;
font-family: 'Nova Square';
font-size: 5rem;
font-weight: bolder;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
text-shadow: -7px 7px #333;
}
#canvas {
background-color: #4d4d4d;
display: block;
margin: auto;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
animation-duration: 0.1s;
animation-fill-mode: forwards;
}
#keyframes animate {
0% {left: 0; right: 0;}
25% {left: 50px; right: 0;}
50% {left: 0; right: 50px;}
75% {left: 50px; right: 0;}
100% {left: 0; right: 0;}
}
.score {
width: 80px;
height: 80px;
margin-left: auto;
margin-right: auto;
color: white;
font-family: 'Nova Square';
font-size: 4rem;
display: flex;
justify-content: center;
align-items: center;
margin-top: 50px;
}
</style>
</head>
<body>
<div class="score">0</div>
<canvas id="canvas" width="784" height="528"></canvas>
<div class="game-over">Game Over</div>
<script src="script.js"></script>
</body>
</html>

You can add a variable to hold the next direction, then update the actual direction from draw, so it will be in synch with what is actually happening:
//declare global variables
const canvas = document.querySelector('#canvas');
const score = document.querySelector('.score');
const gameOver = document.querySelector('.game-over');
//set canvas context
const ctx = canvas.getContext('2d');
//put canvas dimensions into variables
const cvsW = canvas.width;
const cvsH = canvas.height;
//load audio files
let eat = new Audio();
let dead = new Audio();
eat.src = 'audio/eat.mp3';
dead.src = 'audio/dead.mp3';
//create snake unit
const unit = 16;
//create points variable
let points = 0;
//create snake and set starting position
let snake = [{
x : cvsW/2,
y : cvsH/2
}]
//create food object and set its position somewhere on board
let food = {
//Math.floor(Math.random()*cvsW + 1)---number from 1 to 784
//Math.floor(Math.random()*cvsW/unit + 1)---number from 1 to 79
//Math.floor(Math.random()*cvsW/unit + 1)*unit---number from 1 to 784(but it's a multiple of unit)
//Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit---same as above but -1 keeps food inside canvas
x : Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit-unit/2,
y : Math.floor(Math.random()*(cvsH/unit - 1)+1)*unit-unit/2
}
//create a variable to store the direction of the snake
let direction;
let nextDirection
//add event to read users input then change direction
document.addEventListener('keydown', (e) => {
if(e.keyCode == 37 && direction != 'right') nextDirection = 'left';
else if (e.keyCode == 38 && direction != 'down') nextDirection = 'up';
else if (e.keyCode == 39 && direction != 'left') nextDirection = 'right';
else if (e.keyCode == 40 && direction != 'up') nextDirection = 'down';
})
function draw() {
//clear canvas and redraw snake
ctx.clearRect(0, 0, cvsW, cvsH);
for(let i = 0; i < snake.length; i++) {
ctx.fillStyle = 'limegreen';
ctx.fillRect(snake[i].x-unit/2, snake[i].y-unit/2, unit, unit);
}
//draw food
ctx.fillStyle = 'red';
ctx.fillRect(food.x-unit/2, food.y-unit/2, unit, unit);
//grab heads position
let headX = snake[0].x;
let headY = snake[0].y;
//move snake in chosen direction
if(nextDirection == 'left') headX -= unit;
else if(nextDirection == 'right') headX += unit;
else if(nextDirection == 'up') headY -= unit;
else if(nextDirection == 'down') headY += unit;
// update the direction the snake is travelling:
direction = nextDirection;
//create new snake unit
let newHead = {x : headX, y :headY}
//check to see if snake has hit a wall or itself
if(headX < 0 || headX > cvsW || headY < 0 || headY > cvsH || collision(headX, headY)) {
dead.play();
clearInterval(runGame);
canvas.style.animationName = 'animate';
setTimeout(function() {gameOver.style.opacity = '1';}, 150);
}
//check to see if snakes eaten food
if(headX === food.x && headY === food.y) {
eat.play();
//increase score
points++;
score.innerText = points;
//get new food unit
getFood();
//create 3 new units
for(let i = 3; i > 0; i--) {
//add those units -without this code snake will not grow
snake.unshift(newHead);
}
} else {
//remove tail -without this code snake will keep growing
snake.pop();
}
//add new head position -without this code snake will not move
snake.unshift(newHead);
}
let runGame = setInterval(draw, 65);
function collision(x, y) {
for(let i = 1; i < snake.length; i++) {
if(x == snake[i].x && y == snake[i].y) return true;
}
return false;
}
function getFood() {
food = {
x : Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit-unit/2,
y : Math.floor(Math.random()*(cvsH/unit - 1)+1)*unit-unit/2
}
//loop through snake to see if food generates inside snake
for(let i = 0; i < snake.length; i++) {
//if so call the function again
if(food.x == snake[i].x && food.y == snake[i].y) return getFood();
}
//else return new random point
return food;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Snake</title>
<link href="https://fonts.googleapis.com/css?family=Nova+Square" rel="stylesheet">
<style>
body {
background-color: #333;
}
.game-over {
z-index: 1;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
width: 784px;
height: 528px;
margin: auto;
color: red;
font-family: 'Nova Square';
font-size: 5rem;
font-weight: bolder;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
text-shadow: -7px 7px #333;
}
#canvas {
background-color: #4d4d4d;
display: block;
margin: auto;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
animation-duration: 0.1s;
animation-fill-mode: forwards;
}
#keyframes animate {
0% {left: 0; right: 0;}
25% {left: 50px; right: 0;}
50% {left: 0; right: 50px;}
75% {left: 50px; right: 0;}
100% {left: 0; right: 0;}
}
.score {
width: 80px;
height: 80px;
margin-left: auto;
margin-right: auto;
color: white;
font-family: 'Nova Square';
font-size: 4rem;
display: flex;
justify-content: center;
align-items: center;
margin-top: 50px;
}
</style>
</head>
<body>
<div class="score">0</div>
<canvas id="canvas" width="784" height="528"></canvas>
<div class="game-over">Game Over</div>
<script src="script.js"></script>
</body>
</html>

Related

JavaScript Snake Game with two Snakes (Local Mutiplayer)

Im programing a Snake Webgame with HTML, CSS and JavaScript and im implementing Multiple Gamemodes, one should be a Local Multiplayer where one Person is playing with Arrow keys and the Other Person with WASD.
But I got the problem, that I dont know how to Summon a Second Snake. I tried to just copy the Summon Code and rename the variables. But that didn't work, no matter what I tryed.
The code is 90% made by myself, but because of some JavaScript beginner issues I needed some help by the web.
Can someone may tell me how I can summon a Second snake? I just need the "how to" Summon.
let canvas = document.getElementById('game');
let ctx = canvas.getContext('2d');
let grid = 10;
let count = 0;
//Scores
let Score = 0;
let HighScore = 0;
let Alive = true;
//Snake Speed
let Speed = 15;
//Snake Base Stats
let snake = {
x: 200,
y: 200,
dx: grid,
dy: 0,
cells: [],
maxCells: 4
};
//First Apple Spawn
let apple = {
x: 320,
y: 320
};
//Random Int for Apple Spawn
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
function loop() {
requestAnimationFrame(loop);
//Tic speed
if (++count < Speed) {
return;
}
count = 0;
ctx.clearRect(0, 0, canvas.width, canvas.height);
snake.x += snake.dx;
snake.y += snake.dy;
//Automatic Movement
if (snake.x < 0) {
snake.x = canvas.width - grid;
} else if (snake.x >= canvas.width) {
snake.x = 0;
}
if (snake.y < 0) {
snake.y = canvas.height - grid;
} else if (snake.y >= canvas.height) {
snake.y = 0;
}
snake.cells.unshift({
x: snake.x,
y: snake.y
});
if (snake.cells.length > snake.maxCells) {
snake.cells.pop();
}
//Apple Color
ctx.fillStyle = 'gold';
ctx.fillRect(apple.x, apple.y, grid - 1, grid - 1);
//Snake Color
ctx.fillStyle = 'white';
snake.cells.forEach(function(cell, index) {
//Snake Color
ctx.fillRect(cell.x, cell.y, grid - 1, grid - 1);
//Snake Eat Apple
if (cell.x === apple.x && cell.y === apple.y) {
snake.maxCells++;
apple.x = getRandomInt(0, 25) * grid;
apple.y = getRandomInt(0, 25) * grid;
SnakeScore();
}
//Snake Dies
for (var i = index + 1; i < snake.cells.length; i++) {
if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) {
SummonSnake();
Alive = false;
SnakeScore();
}
}
});
}
//Arrow Key Movement
document.addEventListener('keydown', function(e) {
if (e.which === 37 && snake.dx === 0) {
snake.dx = -grid;
snake.dy = 0;
} else if (e.which === 38 && snake.dy === 0) {
snake.dy = -grid;
snake.dx = 0;
} else if (e.which === 39 && snake.dx === 0) {
snake.dx = grid;
snake.dy = 0;
} else if (e.which === 40 && snake.dy === 0) {
snake.dy = grid;
snake.dx = 0;
}
});
requestAnimationFrame(loop);
//Score
function SnakeScore() {
if (Alive === true) {
Score++;
document.getElementById("Score").innerHTML = Score;
} else if (Alive === false) {
Score = 0;
document.getElementById("Score").innerHTML = Score;
Alive = true;
}
if (Score > HighScore) {
SnakeHighscore();
}
}
//Highscore
function SnakeHighscore() {
HighScore = Score;
document.getElementById("Highscore").innerHTML = HighScore;
}
//Snake Summon Stats
function SummonSnake() {
snake.x = 200;
snake.y = 200;
snake.cells = [];
snake.maxCells = 4;
snake.dx = grid;
snake.dy = 0;
}
//Gamemodes
function GameMode() {
value = document.getElementById('valueGames').value;
if (value === "E") {
Speed = 15;
Score = 0;
document.getElementById("Score").innerHTML = Score;
SummonSnake();
} else if (value === "M") {
Speed = 10;
Score = 0;
document.getElementById("Score").innerHTML = Score;
SummonSnake();
} else if (value === "H") {
Speed = 5;
Score = 0;
document.getElementById("Score").innerHTML = Score;
SummonSnake();
} else if (value === "Multiplayer") {
}
}
body {
display: flex;
align-items: center;
justify-content: center;
background-image: url('https://wallpapercave.com/wp/wp3493594.png');
}
canvas {
width: 403px;
height: 403px;
border: 2px solid rgb(255, 213, 0);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#ScoreCSS {
margin-right: 50px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="main.css">
<meta charset="UTF-8">
<title>Snake NICEEE</title>
</head>
<body>
<!-- Scores -->
<h1 id="ScoreCSS">Score: <a id="Score">0</a></h1>
<h1>Highscore: <a id="Highscore">0</a></h1>
<!-- Canvas (Game Field)-->
<canvas id="game" width="400" height="400"></canvas>
<!-- Dropdown for GameModes -->
<select id="valueGames" onchange="GameMode()">
<option value="E" selected>Easy</option>
<option value="M">Middle</option>
<option value="H">Hard</option>
<option value="Multiplayer">Multiplayer</option>
</select>
</body>
</html>
This should get you started. I created a second snake object and moved the alive property to the snake. Then I added functions to draw/move the snake and passed the snake object to those functions so you don't have as much duplicate code in order to handle multiple snakes. (Theoretically, you could add more than 2 snakes this way by creating new snake objects and then adding moveSnake(snake3, canvas); etc).
You'll need to add the event listeners for WASD as well as "dead" game logic in Multiplayer mode, because presumably the game will pause somehow and show the winner and the high score.
let canvas = document.getElementById('game');
let ctx = canvas.getContext('2d');
let grid = 10;
let count = 0;
//Scores
let Score = 0;
let HighScore = 0;
let Speed = 15;
//Snake Base Stats
let snake = {
x: 200,
y: 200,
dx: grid,
dy: 0,
cells: [],
maxCells: 4,
alive: true, // moved the global var here
active: true, // added this in order to track multiple snakes being active
color: 'white' // moved this here to define it per-snake
};
let snake2 = {
x: 200,
y: 200,
dx: grid,
dy: 0,
cells: [],
maxCells: 4,
alive: true,
active: false,
color: 'red'
};
// keep track of active snakes
let snakes = [
snake
];
//First Apple Spawn
let apple = {
x: 320,
y: 320,
color: 'gold'
};
//Random Int for Apple Spawn
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
function loop() {
requestAnimationFrame(loop);
//Tic speed
if (++count < Speed) {
return;
}
count = 0;
ctx.clearRect(0, 0, canvas.width, canvas.height);
moveSnakes(snakes, canvas);
//Apple Color
ctx.fillStyle = apple.color;
ctx.fillRect(apple.x, apple.y, grid - 1, grid - 1);
drawSnakes(snakes, ctx, apple);
}
//Arrow Key Movement
document.addEventListener('keydown', function(e) {
if (e.which === 37 && snake.dx === 0) {
snake.dx = -grid;
snake.dy = 0;
} else if (e.which === 38 && snake.dy === 0) {
snake.dy = -grid;
snake.dx = 0;
} else if (e.which === 39 && snake.dx === 0) {
snake.dx = grid;
snake.dy = 0;
} else if (e.which === 40 && snake.dy === 0) {
snake.dy = grid;
snake.dx = 0;
}
});
//TODO: add movement for snake2
requestAnimationFrame(loop);
function moveSnakes(snakes, canvas) {
snakes.forEach(function(snake) {
if (!snake.active) {
return;
}
snake.x += snake.dx;
snake.y += snake.dy;
if (snake.x < 0) {
snake.x = canvas.width - grid;
} else if (snake.x >= canvas.width) {
snake.x = 0;
}
if (snake.y < 0) {
snake.y = canvas.height - grid;
} else if (snake.y >= canvas.height) {
snake.y = 0;
}
snake.cells.unshift({
x: snake.x,
y: snake.y
});
if (snake.cells.length > snake.maxCells) {
snake.cells.pop();
}
});
}
function drawSnakes(snakes, ctx, apple) {
snakes.forEach(function(snake) {
if (!snake.active) {
return;
}
//Snake Color
ctx.fillStyle = snake.color;
snake.cells.forEach(function(cell, index) {
//Snake Color
ctx.fillRect(cell.x, cell.y, grid - 1, grid - 1);
//Snake Eat Apple
if (cell.x === apple.x && cell.y === apple.y) {
snake.maxCells++;
apple.x = getRandomInt(0, 25) * grid;
apple.y = getRandomInt(0, 25) * grid;
SnakeScore(snake);
}
//Snake Dies
for (var i = index + 1; i < snake.cells.length; i++) {
if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) {
SummonSnake(snake);
snake.alive = false;
SnakeScore();
}
}
});
});
}
//Score
function SnakeScore(snake) {
if (snake.alive) {
Score++;
document.getElementById("Score").innerHTML = Score;
} else if (!snake.alive) {
Score = 0;
document.getElementById("Score").innerHTML = Score;
snake.alive = true;
}
if (Score > HighScore) {
SnakeHighscore();
}
}
//Highscore
function SnakeHighscore() {
HighScore = Score;
document.getElementById("Highscore").innerHTML = HighScore;
}
//Snake Summon Stats
function SummonSnake(snake, y) {
snake.x = 200;
snake.y = y;
snake.cells = [];
snake.maxCells = 4;
snake.dx = grid;
snake.dy = 0;
}
function activateSnakes(numberOfSnakes) {
if (numberOfSnakes === 1) {
SummonSnake(snake, 200);
snakes = [snake];
snake2.active = false;
} else if (numberOfSnakes === 2) {
SummonSnake(snake, 180);
SummonSnake(snake2, 220);
snakes = [snake, snake2];
snake2.active = true;
}
}
//Gamemodes
function GameMode() {
value = document.getElementById('valueGames').value;
if (value === "E") {
Speed = 15;
activateSnakes(1);
} else if (value === "M") {
Speed = 10;
activateSnakes(1);
} else if (value === "H") {
Speed = 5;
activateSnakes(1);
} else if (value === "Multiplayer") {
Speed = 5;
activateSnakes(2);
}
Score = 0;
document.getElementById("Score").innerHTML = Score;
}
GameMode();
body {
display: flex;
align-items: center;
justify-content: center;
background-image: url('https://wallpapercave.com/wp/wp3493594.png');
}
canvas {
width: 403px;
height: 403px;
border: 2px solid rgb(255, 213, 0);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#ScoreCSS {
margin-right: 50px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="main.css">
<meta charset="UTF-8">
<title>Snake NICEEE</title>
</head>
<body>
<!-- Scores -->
<h1 id="ScoreCSS">Score: <a id="Score">0</a></h1>
<h1>Highscore: <a id="Highscore">0</a></h1>
<!-- Canvas (Game Field)-->
<canvas id="game" width="400" height="400"></canvas>
<!-- Dropdown for GameModes -->
<select id="valueGames" onchange="GameMode()">
<option value="E">Easy</option>
<option value="M">Middle</option>
<option value="H">Hard</option>
<option value="Multiplayer" selected>Multiplayer</option>
</select>
</body>
</html>

How to make a self playing snake game using JS?

I'm trying to code a snake game that will randomly move through the canvas. for now, I won't worry about the "food" as my main problem is the self-playing part not running. it seems to start but does not change course over time, so not sure if need to implement a timer (?)
Tried with a switch and thought by generating with random() one of the alternatives but only goes in one direction until hits the border
*/<-------------------------HTML----------------------->*/
<html>
<head>
<meta charset='utf-8'/>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class= 'game'>
<div id = 'home'>
<canvas id='mycanvas' width='350' height='350'>
</canvas>
</div>
<button id='btn'>START</button>
</div>
<script src="js/logic.js"></script>
</body>
</html>
CSS:
/*<----------------------CSS----------------------->*/
#home {
width: 350px;
height: 350px;
background-size: auto 350px;
background-repeat: no-repeat;
background-color: lightgrey;
background-position: center center;
padding: 0;
margin: 03;
}
button {
background-color: green;
color: white;
border: none;
bottom: 0;
height: 30px;
font-size: 12pt;
float: left;
width: 90px;
margin: 10px 0 0 0;
}
button:hover {
background-color: darkgreen;
}
button:disabled {
background-color: grey;
}
.game {
margin: 0 auto;
}
JS:
/*<-------------------JS----------------->*/
var mycanvas = document.getElementById('mycanvas');
var ctx = mycanvas.getContext('2d');
var snakeSize = 10;
var w = 350;
var h = 350;
var snake;
var snakeSize = 10;
var pixel;
var drawModule = (function() {
var bodySnake = function(x, y) {
ctx.fillStyle = 'green';
ctx.fillRect(x * snakeSize, y * snakeSize, snakeSize, snakeSize);
ctx.strokeStyle = 'black';
ctx.strokeRect(x * snakeSize, y * snakeSize, snakeSize, snakeSize);
}
var drawSnake = function() {
var length = 4;
snake = [];
for (var i = length - 1; i >= 0; i--) {
snake.push({ x: i, y: 0 });
}
}
var paint = function() {
ctx.fillStyle = 'lightgrey';
ctx.fillRect(0, 0, w, h);
ctx.strokeStyle = 'black';
ctx.strokeRect(0, 0, w, h);
btn.setAttribute('disabled', true);
var snakeX = snake[0].x;
var snakeY = snake[0].y;
if (direction == 'right') {
snakeX++;
} else if (direction == 'left') {
snakeX--;
} else if (direction == 'up') {
snakeY--;
} else if (direction == 'down') {
snakeY++;
}
if (snakeX == -1 || snakeX == w / snakeSize || snakeY == -1 || snakeY == h / snakeSize || checkCollision(snakeX, snakeY, snake)) {
btn.removeAttribute('disabled', true);
ctx.clearRect(0, 0, w, h);
gameloop = clearInterval(gameloop);
return;
}
if (snakeX == pixel.x && snakeY == pixel.y) {
var tail = { x: snakeX, y: snakeY };
} else {
var tail = snake.pop();
tail.x = snakeX;
tail.y = snakeY;
}
snake.unshift(tail);
for (var i = 0; i < snake.length; i++) {
bodySnake(snake[i].x, snake[i].y);
}
}
var createPixels = function() {
pixel = {
x: Math.floor((Math.random() * 30) + 1),
y: Math.floor((Math.random() * 30) + 1)
}
}
var checkCollision = function(x, y, array) {
for (var i = 0; i < array.length; i++) {
if (array[i].x === x && array[i].y === y) {
return true;
//this part should reinitiate the game
//when it hits an edge
/*}
if (x > w - 1 || x < 0 || y > h - 1 || h < 0) {
return true;*/
}
return false;
}
}
var init = function() {
var r = Math.round(Math.random() * 5);
switch (r) {
case 1:
direction = 'left';
console.log('left');
break;
case 2:
direction = 'right';
console.log('right');
break;
case 3:
direction = 'up';
console.log('up');
break;
case 4:
direction = 'down';
console.log('down');
break;
}
drawSnake();
createPixels();
gameloop = setInterval(paint, 80);
}
return {
init: init
};
}());
(function(window, document, undefined) {
var btn = document.getElementById('btn');
btn.addEventListener("click", function() { drawModule.init(); });
}
)(window, document, drawModule);
Assign direction a random value inside the paint() function.
Explanation: Currently, the only call to random() is in the init() function, which is only called once. Thus direction is only set once, and there is no way for it to change.

pop function isn't taking tail off snake array?

I am making a snake game in JS. Right now I'm able to draw the snake to the canvas and accept a direction to move from the user. Given the direction I'm able to unshift() a new head to the snake, but for some reason I can't use the pop method to remove the tail. This just results in my snake growing bigger and bigger. Any ideas to why this is?
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
//set canvas dimension equal to css dimension
canvas.width = 768;
canvas.height = 512;
//now put those dimensions into variables
const cvsW = canvas.width;
const cvsH = canvas.height;
//create snake unit
const unit = 16;
//create snake array
let snake = [{x: cvsW/2, y: cvsH/2}];
//read user's direction
document.addEventListener('keydown', changeDirection);
let direction;
function changeDirection(e) {
if (e.keyCode == 37 && direction != 'right') direction = 'left';
else if (e.keyCode == 38 && direction != 'down') direction = 'up';
else if (e.keyCode == 39 && direction != 'left') direction = 'right';
else if (e.keyCode == 40 && direction != 'up') direction = 'down';
console.log(direction);
}
function draw() {
for(let i = 0; i < snake.length; i++) {
ctx.fillStyle = 'limegreen';
ctx.fillRect(snake[i].x, snake[i].y, unit, unit);
}
//grab head position
let headX = snake[0].x;
let headY = snake[0].y;
snake.pop();
if(direction == 'left') headX -= unit;
else if(direction == 'up') headY -= unit;
else if(direction == 'right') headX += unit;
else if(direction == 'down') headY += unit;
//create new head
let newHead = {x: headX, y: headY}
//add head to snake
snake.unshift(newHead);
}
setInterval(draw, 100);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Snake Game</title>
<style>
body {
background-color: #333;
}
canvas {
background-color: #4d4d4d;
margin: auto;
display: block;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 750px;
height: 500px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="script.js"></script>
</body>
</html>
You need to clear the current canvas every iteration, else pixels painted onto the canvas earlier will remain. Add
ctx.clearRect(0, 0, canvas.width, canvas.height);
right before you start iterating over the snake array to paint each square:
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
//set canvas dimension equal to css dimension
canvas.width = 768;
canvas.height = 512;
//now put those dimensions into variables
const cvsW = canvas.width;
const cvsH = canvas.height;
//create snake unit
const unit = 16;
//create snake array
let snake = [{
x: cvsW / 2,
y: cvsH / 2
}];
//read user's direction
document.addEventListener('keydown', changeDirection);
let direction;
function changeDirection(e) {
if (e.keyCode == 37 && direction != 'right') direction = 'left';
else if (e.keyCode == 38 && direction != 'down') direction = 'up';
else if (e.keyCode == 39 && direction != 'left') direction = 'right';
else if (e.keyCode == 40 && direction != 'up') direction = 'down';
console.log(direction);
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'limegreen';
for (let i = 0; i < snake.length; i++) {
ctx.fillRect(snake[i].x, snake[i].y, unit, unit);
}
//grab head position
let headX = snake[0].x;
let headY = snake[0].y;
snake.pop();
if (direction == 'left') headX -= unit;
else if (direction == 'up') headY -= unit;
else if (direction == 'right') headX += unit;
else if (direction == 'down') headY += unit;
//create new head
let newHead = {
x: headX,
y: headY
}
//add head to snake
snake.unshift(newHead);
}
setInterval(draw, 100);
body {
background-color: #333;
}
canvas {
background-color: #4d4d4d;
margin: auto;
display: block;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 750px;
height: 500px;
}
<canvas id="canvas"></canvas>
Rearrange your draw function and add ctx.clearRect . Modify the draw function in the following way:
function draw() {
//grab head position
let headX = snake[0].x;
let headY = snake[0].y;
if(direction == 'left') headX -= unit;
else if(direction == 'up') headY -= unit;
else if(direction == 'right') headX += unit;
else if(direction == 'down') headY += unit;
//create new head
let newHead = {x: headX, y: headY}
//add head to snake
snake.unshift(newHead);
for(let i = 0; i < snake.length; i++) {
ctx.fillStyle = 'limegreen';
ctx.fillRect(snake[i].x, snake[i].y, unit, unit);
}
let popped = snake.pop();
ctx.clearRect(popped.x, popped.y, unit, unit);
}

snake can't move on the very edge of the canvas in my JS snake game

Hey guys I'm making a snake game in JS. The original problem i was working on was trying to get the game to stop running by using clearInterval(game); once the snake head makes contact with the edge of the canvas. Once I figured that out it created a new problem. You can't ride the snake on the edge of the canvas. This is a problem because the food will generate on the edge sometimes. Any ideas, I'm stumped. Thanks:)
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
//set canvas dimension equal to css dimension
canvas.width = 768;
canvas.height = 512;
//now put those dimensions into variables
const cvsW = canvas.width;
const cvsH = canvas.height;
//create snake unit
const unit = 16;
//create snake array
let snake = [{x: cvsW/2, y: cvsH/2}];
//delcare global variable to hold users direction
let direction;
//create food object
let food = {
x : Math.floor(Math.random()*((cvsW/unit)-1)+1)*unit,
y : Math.floor(Math.random()*((cvsH/unit)-1)+1)*unit
}
//read user's direction
document.addEventListener('keydown', changeDirection);
function changeDirection(e) {
//set direction
if (e.keyCode == 37 && direction != 'right') direction = 'left';
else if (e.keyCode == 38 && direction != 'down') direction = 'up';
else if (e.keyCode == 39 && direction != 'left') direction = 'right';
else if (e.keyCode == 40 && direction != 'up') direction = 'down';
}
function draw() {
//refresh canvas
ctx.clearRect(0, 0, cvsW, cvsH);
//draw snake
for(let i = 0; i < snake.length; i++) {
ctx.fillStyle = 'limegreen';
ctx.fillRect(snake[i].x, snake[i].y, unit, unit);
}
//grab head position
let headX = snake[0].x;
let headY = snake[0].y;
//check if snake hit wall
if(headX <= 0 || headY <= 0 || headX >= (cvsW-unit) || headY >= (cvsH-unit)) {
clearInterval(runGame);
}
//posistion food on board
ctx.fillStyle = 'red';
ctx.fillRect(food.x, food.y, unit, unit);
//send the snake in chosen direction
if(direction == 'left') headX -= unit;
else if(direction == 'up') headY -= unit;
else if(direction == 'right') headX += unit;
else if(direction == 'down') headY += unit;
//create new head
let newHead = {x: headX, y: headY}
if(headX == food.x && headY == food.y) {
//create new food position
food = {
x : Math.floor(Math.random()*((cvsW/unit)-1)+1)*unit,
y : Math.floor(Math.random()*((cvsH/unit)-1)+1)*unit
}
//add 3 units to the snake
snake.unshift(newHead);
snake.unshift(newHead);
snake.unshift(newHead);
}
else {
//remove tail
snake.pop();
}
//add head to snake
snake.unshift(newHead);
}
//run game engine
let runGame = setInterval(draw, 70);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Snake Game</title>
<style>
body {
background-color: #333;
}
canvas {
background-color: #4d4d4d;
margin: auto;
display: block;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 750px;
height: 500px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="script.js"></script>
</body>
</html>

How to clear image right into next html canvas games?

I made a game, a race car game. The code is:
index.html
<!DOCTYPE html>
<html>
<head>
<title> Race car </title>
<link rel="stylesheet" type="text/css" href="style.css">
<script>
var ctx;
//save the parameters to variables for easier clearing
var x_Car = 30,
y_Car = 95,
width_Car = 110,
height_Car = 55,
imgCar = new Image();
imgCar.src = "cars/race-car.png";
var nrMonsters = 0,
imgMonster = new Image();
imgMonster.src = "Monster/yellow_monster.png";
var width_Monster = 100;
var height_Monster = 20;
var x_Monster = 50;
var y_Monster = 5;
var speed = 1;
// function drawMonster() {
// ctx.drawImage(imgMonster, x_Monster, y_Monster, width_Monster, height_Monster);
// }
function drawCar() {
ctx.drawImage(imgCar, x_Car, y_Car, width_Car, height_Car);
}
function rand() {
var left = 50;
var right = 150;
var z;
var random = Math.random();
if (random > 0.5) {
z = right;
} else {
z = left;
}
return z;
}
valX = rand();
function draw(x, y) {
//ctx.save();
if (valX == 50 && x_Car > 75) {
ctx.clearRect(x + 5, -5, 150, 150); // 95 cu 150
} else if (valX == 50) {
ctx.clearRect(x + 5, y, 150, 10);
} else if (valX == 150 && x_Car < 32){
ctx.clearRect(x + 150, -5, 260, 150); // 95 cu 150
} else if (valX == 150) {
ctx.clearRect(x + 150, y, 260, 10);
}
ctx.drawImage(imgMonster, valX, y, width_Monster, height_Monster);
ctx.restore();
y += speed;
if (y == y_Car && x_Car < 32 && valX == 50
|| y == y_Car && x_Car > 75 && valX == 150
|| y > y_Car && y < y_Car + height_Car && x_Car < 32 && valX == 50
|| y > y_Car && y < y_Car + height_Car && x_Car > 75 && valX == 150) {
window.location.href = "you_lost_the_game.html";
// alert("You lost the game");
}
var loopTimer = setTimeout("draw(" + x + ", " + y + ")", 20);
if (y == 150 && nrMonsters < 15
|| y == 160 && nrMonsters >= 15) {
valX = rand();
nrMonsters++;
console.log(nrMonsters);
document.getElementById("numarObstacole").innerHTML = nrMonsters;
if (nrMonsters == 5
|| nrMonsters == 10
|| nrMonsters == 20
|| nrMonsters == 25) {
// || nrMonsters == 20 || nrMonsters == 27) {
speed += 1;
} else if (nrMonsters == 15) {
speed += 1;
}
draw(0, 0);
if (nrMonsters == 25) {
window.location.href = "win.html";
}
}
}
function init() {
ctx = document.getElementById("canvas").getContext("2d");
// audio
// myAudio = new Audio('http://static1.grsites.com/archive/sounds/cars/cars002.wav');
myAudio = new Audio("Sounds/Get low NFS.mp3");
myAudio.addEventListener("ended", function() {
this.currentTime = 0;
this.play();
}, false);
myAudio.play();
//draw wall
draw(0, 0);
drawCar();
function moveRight() {
ctx.clearRect(x_Car, y_Car, width_Car, height_Car);
x_Car = x_Car + 120;
if(x_Car > 220){
x_Car -= 120;
drawCar();
} else {
drawCar();
}
}
function moveLeft() {
ctx.clearRect(x_Car, y_Car, width_Car, height_Car);
x_Car = x_Car-120;
if (x_Car < 20) {
x_Car += 120;
drawCar();
} else {
drawCar();
}
}
function moveUp() {
ctx.clearRect(x_Car, y_Car, width_Car, height_Car);
y_Car = y_Car-20;
drawCar();
}
function moveDown() {
ctx.clearRect(x_Car, y_Car, width_Car, height_Car);
y_Car = y_Car + 20;
drawCar();
}
window.addEventListener("keypress", checkKeyPressed, false);
function checkKeyPressed(e) {
if (e.charCode == "97" || e.charCode == 027) {
moveLeft();
}
if (e.charCode == "100" || e.charCode== 026) {
moveRight();
}
if (e.charCode == "119" || e.charCode== 024) {
moveUp();
}
if (e.charCode == "115" || e.charCode== 025) {
moveDown();
}
}
}
function moveRight() {
ctx.clearRect(x_Car, y_Car, width_Car, height_Car);
x_Car = x_Car + 120;
if (x_Car > 220) {
x_Car -= 120;
drawCar();
} else {
drawCar();
}
}
function moveLeft() {
ctx.clearRect(x_Car, y_Car, width_Car, height_Car);
x_Car = x_Car - 120;
if(x_Car < 20) {
x_Car += 120;
drawCar();
} else {
drawCar();
}
}
function moveUp() {
ctx.clearRect(x_Car, y_Car, width_Car, height_Car);
y_Car = y_Car - 20;
drawCar();
}
function moveDown() {
ctx.clearRect(x_Car, y_Car, width_Car, height_Car);
y_Car = y_Car + 20;
drawCar();
}
window.addEventListener("keypress", checkKeyPressed, false);
function checkKeyPressed(e) {
if (e.charCode == "97") {
moveLeft();
}
if (e.charCode == "100") {
moveRight();
}
if (e.charCode == "119") {
moveUp();
}
if (e.charCode == "115") {
moveDown();
}
}
</script>
</head>
<body onLoad="init();">
<div id="moveButons">
<table>
<tr>
<td></td>
<td>
<input class="button" id="up" type="button" value="Move Up" onclick="moveUp()"/>
</td>
<td></td>
</tr>
<tr>
<td>
<input class="button" id="left" type="button" value="Move Left" onclick="moveLeft()"/>
</td>
<td></td>
<td>
<input class="button" id="right" type="button" value="Move Right" onclick="moveRight()"/>
</td>
</tr>
<tr>
<td></td>
<td>
<input class="button" id="down" type="button" value="Move Down" onclick="moveDown()"/>
</td>
<td></td>
</tr>
</table>
</div>
<p class="numarObstacole">
Ai trecut peste <span id="numarObstacole"> 0 </span> obstacole
</p>
<canvas id="canvas">
Your browser doesn't support the HTML5 element canvas.
</canvas>
</body>
</html>
style.css
* {
margin: 0;
padding: 0;
}
body {
background-color: yellow;
}
#canvas {
background: white;
margin-left: 50%;
position: absolute;
left: -206px;
width: 412px;
height: 100%;
background-image: url("street/street (2).jpg");
background-position: 0px 0px;
background-repeat: repeat-y;
-webkit-animation: animatedBackground 5s linear infinite;
-moz-animation: animatedBackground 5s linear infinite;
animation: animatedBackground 5s linear infinite;
}
#moveButons {
position: absolute;
left: 20px;
top: 50px;
}
#-webkit-keyframes animatedBackground {
from { background-position: 0 100%; }
to { background-position: 0 0; }
}
#-moz-keyframes animatedBackground {
from { background-position: 0 100%; }
to { background-position: 0 0; }
}
#keyframes animatedBackground {
from { background-position: 0 100%; }
to { background-position: 0 0; }
}
#lostGame {
text-align: center;
color: red;
margin-top: 175px;
}
#win {
text-align: center;
color: blue;
margin-top: 175px;
}
#canvas_lost {
width: 320px;
height: 240px;
background: url("Joker/the-joker-laughing-1.jpg");
margin-left: 50%;
position: absolute;
left: -160px;
}
#canvas_win {
width: 320px;
height: 240px;
background: url("Joker/joker.gif");
margin-left: 50%;
position: absolute;
left: -160px;
}
.button {
color: blue;
font-size: 17px;
text-align: center;
background: #6BA7FA;
width: 100px;
}
.numarObstacole {
position: absolute;
color: #1F1;
background: silver;
font-size: 20px;
font-family: Serif;
}
#numarObstacole {
color: blue;
font-size: 22px;
font-family: sans-serif;
}
win.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<title> Win </title>
<script>
function init() {
var canvas = document.getElementById("canvas_win");
var ctx = canvas.getContext("2d");
var myAudio = new Audio("Sounds/Fireworks Finale.mp3");
myAudio.addEventListener("ended", function() {
this.currentTime = 0;
this.play();
}, false);
myAudio.play();
var otherAudio = new Audio("Sounds/Audience_Applause.mp3");
otherAudio.addEventListener("ended", function() {
this.currentTime = 0;
this.play();
}, false);
otherAudio.play()
}
</script>
</head>
<body onLoad="init();">
<h1 id="win"> You win </h1>
<canvas id="canvas_win">
Your browser doesn't support the HTML5 element canvas.
</canvas>
</body>
</html>
you_lost_the_game.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<title> You lost the game </title>
<script>
function init() {
var canvas = document.getElementById("canvas_lost");
var ctx = canvas.getContext("2d");
var myAudio = new Audio("Sounds/hahaha-Peter_De_Lang.mp3");
myAudio.addEventListener("ended", function() {
this.currentTime = 0;
this.play();
}, false);
myAudio.play();
}
</script>
</head>
<body onLoad="init();">
<h1 id="lostGame"> You lost the game </h1>
<canvas id="canvas_lost">
Your browser doesn't support the HTML5 element canvas.
</canvas>
</body>
</html>
The problem is with the monster image.
When this image comes to the car image with a bigger speed, it leaves behind some "trail".
How can I solve it?
The answer
I believe your issue is with your clearing with clearRect. Instead of carefully finding specific places to clear the canvas (i.e. ctx.clearRect(x + 150, -5, 260, 150);), you should just up and do:
// Get <canvas> on a variable
var canvas = document.getElementById("canvas");
// ctx is still the same
var ctx = canvas.getContext("2d");
function draw(x, y) {
// Clears everything on the canvas on every loop
ctx.clearRect(0, 0, canvas.width, canvas.height);
...
That way, any "trail" left by the car or by the monster will be cleared on every frame.
Now, for coding suggestions:
Instead of adding an event listener for when an audio ends, simply turn the loop property to true. Here's some code:
// Your current code
var myAudio = new Audio("Sounds/---.mp3");
myAudio.addEventListener("ended", function() {
this.currentTime = 0;
this.play();
}, false);
myAudio.play();
// Some smaller and more efficient code
var myAudio = new Audio("Sounds/---.mp3");
myAudio.loop = true; // Restarts the audio every time it reaches the end
myAudio.play();
This code doesn't make much sense:
if (nrMonsters == 5
|| nrMonsters == 10
|| nrMonsters == 20
|| nrMonsters == 25) {
// || nrMonsters == 20 || nrMonsters == 27) {
speed += 1;
} else if (nrMonsters == 15) {
speed += 1;
}
If something do this otherwise do the same thing? Here's a quick fix:
if (nrMonsters == 5
|| nrMonsters == 10
|| nrMonsters == 15
|| nrMonsters == 20
|| nrMonsters == 25) {
speed += 1;
}
You don't need this else here:
// From the moveRight() function
if (x_Car > 220) {
x_Car -= 120;
drawCar();
} else {
drawCar();
}
You see, if you're going to drawCar() anyway, you don't need to put it inside the if. Here's better code:
if (x_Car > 220) {
x_Car -= 120;
}
drawCar();
You shouldn't have those leading zeroes:
// From the checkKeyPressed() function
if (e.charCode == "97" || e.charCode == 027) { ... }
That 027 (and some subsequent numbers with leading a 0) gets treated as a base 8 number. I'm pretty sure it wasn't your intention to have an octal number there, but if that's the case, then the proper formatting is prepeding it with 0o instead of simply a zero.
var binal = 0b10; // (0b)10 in base 2 is equal to 2 in decimal
var octal = 0o10; // (0o)10 in base 8 is equal to 8 in decimal
var hexal = 0x10; // (0x)10 in base 16 is equal to 16 in decimal
That's all that I found. I wish you good luck with your game! Cheers!

Categories

Resources