smooth snake movement in javascript - javascript

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>

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>

Difficulty Adding a "Play Again!" Button to this snake game

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.

How to reset javascript game when popup window is closed

This snake style game is opened in a popup when you click a button on a page, then you are given a start screen and instructions, you click start to begin the game, and when you lose it displays your score and some text, if you close the popup and reopen it, it still shows the score rather than the start screen.
How do I make it reset when the popup is closed?
jQuery(document).ready(function($) {
$("#canvas").addClass("displayNone");
$("#lose").addClass("displayNone");
$("#post").removeClass("displayNone");
$("#controls").removeClass("displayNone");
$('#play').on('click', function() {
$('#playdiv').addClass('show-game');
})
var canvas = $("#canvas")[0];;
var ctx = canvas.getContext("2d");
var w = $("#canvas").width();
var h = $("#canvas").height();
var cw = 10;
var d;
var food;
var score;
var snake_array;
$("#score").addClass("displayNone");
$("#start").click(function() {
$("#start").addClass("displayNone");
$("#lose").addClass("displayNone");
$("#post").addClass("displayNone");
$("#canvas").removeClass("displayNone");
init();
})
var keys = [];
window.addEventListener("keydown",
function(e) {
keys[e.keyCode] = true;
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40:
case 32:
e.preventDefault();
break;
default:
break;
}
},
false);
window.addEventListener('keyup',
function(e) {
keys[e.keyCode] = false;
},
false);
// was here
function init() {
d = "right";
create_snake();
create_food();
score = 0;
if (typeof game_loop != "undefined") clearInterval(game_loop);
game_loop = setInterval(paint, 90);
}
// init();
function create_snake() {
var length = 5;
snake_array = [];
for (var i = length - 1; i >= 0; i--) {
snake_array.push({
x: i,
y: 0
});
}
}
function create_food() {
food = {
x: Math.round(Math.random() * (w - cw) / cw),
y: Math.round(Math.random() * (h - cw) / cw),
};
}
function paint() {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, w, h);
ctx.strokeStyle = "black";
ctx.strokeRect(0, 0, w, h);
var nx = snake_array[0].x;
var ny = snake_array[0].y;
if (d == "right") nx++;
else if (d == "left") nx--;
else if (d == "up") ny--;
else if (d == "down") ny++;
if (nx == -1 || nx == w / cw || ny == -1 || ny == h / cw || check_collision(nx, ny, snake_array)) {
document.querySelector('#score').innerText = 'Your score: ' + score;
$("#score").removeClass("displayNone");
$("#lose").removeClass("displayNone");
$("#start").removeClass("displayNone");
$("#canvas").addClass("displayNone");
//init();
return;
}
if (nx == food.x && ny == food.y) {
var tail = {
x: nx,
y: ny
};
score++;
create_food();
} else {
var tail = snake_array.pop();
tail.x = nx;
tail.y = ny;
}
snake_array.unshift(tail);
for (var i = 0; i < snake_array.length; i++) {
var c = snake_array[i];
paint_cell(c.x, c.y);
}
paint_cell(food.x, food.y);
var score_text = "Score: " + score;
ctx.fillText(score_text, 5, h - 5);
}
function paint_cell(x, y) {
ctx.fillStyle = "pink";
ctx.fillRect(x * cw, y * cw, cw, cw);
ctx.strokeStyle = "red";
ctx.strokeRect(x * cw, y * cw, cw, cw);
}
function check_collision(x, y, array) {
for (var i = 0; i < array.length; i++) {
if (array[i].x == x && array[i].y == y) return true;
}
return false;
}
$(document).keydown(function(e) {
var key = e.which;
if (key == "37" && d != "right") d = "left";
else if (key == "38" && d != "down") d = "up";
else if (key == "39" && d != "left") d = "right";
else if (key == "40" && d != "up") d = "down";
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div> unknown HTML please post it here</div>
Seems like you never clean setInterval on exit. Please try to clearInterval. Attach to the window, not local valriable.
const window.timerId = setInterval(init, 9000)
// On init
if (window.game_loop != "undefined") clearInterval(game_loop);
window.game_loop = setInterval(paint, 90);

Javascript Collision (Not Collision Detection)

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)

What is the bug in this JavaScript Snake Game?

I used this tutorial: Snake Game HTML5 Game Programming Tutorial javascript to make a similar game.
The problem is about the movement of the snake:
The snake should move properly after pressing a arrow key but it doesn't and it moves automatically by itself.
At the same time, the food (apple) is not visible in the game.
Here is the JS Fiddle with CSS and HTML file included:
https://jsfiddle.net/L734u1y9/#&togetherjs=SeiyP5qa1m
Can somebody help me to find the error?
//Constants
var COLS= 26, ROWS = 26;
//IDs
var EMPTY = 0, SNAKE = 1, FRUIT = 2;
//Directions
var LEFT = 0, UP = 1, RIGHT = 2, DOWN = 3;
//KeyCodes
var KEY_LEFT = 27, KEY_UP = 38, KEY_RIGHT = 39, KEY_DOWN = 40;
var grid= {
width: null,
height:null,
_grid: null,
init: function(d, c, r) {
this.width = c;
this.height = r;
this._grid = [];
for (var x = 0; x < c; x++) {
this._grid.push([]);
for (var y = 0; y < r; y++){
this._grid[x].push(d);
}
}
},
set: function(val, x, y) {
this._grid[x][y] = val;
},
get: function (x, y) {
return this._grid[x][y];
}
};
var snake= {
direction:null,
last: null,
_queue: null,
init: function (d, x, y) {
this.direction = d;
this._queue = [];
this.insert (x, y);
},
insert: function(x, y) {
this._queue.unshift({x:x, y:y});
this.last = this._queue[0];
},
remove: function() {
return this._queue.pop();
}
};
function setFood() {
var empty = [];
for (var x = 0; x < grid.width; x++) {
for (var y = 0; y < grid.height; y++) {
if(grid.get(x, y) === EMPTY) {
empty.push({x:x, y:y});
}
}
}
var randpos = empty[Math.floor(Math.random()*empty.length)];
grid.set(FRUIT, randpos.y);
}
//Game Objects
var canvas, ctx, keystate, frames;
function main() {
canvas = document.createElement("canvas");
canvas.width =COLS*20;
canvas.height = ROWS*20;
ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
frames = 0;
keystate = {};
document.addEventListener("keydown", function(evt) {
keystate[evt.keyCode] = true;
});
document.addEventListener("keyup", function(evt) {
delete keystate[evt.keyCode];
});
init();
loop();
}
function init () {
grid.init(EMPTY,COLS, ROWS);
var sp = {x:Math.floor(COLS/2), y:ROWS-1};
snake.init (UP, sp.x, sp.y);
grid.set(SNAKE, sp.x, sp.y);
setFood();
}
function loop() {
update();
draw();
window.requestAnimationFrame(loop, canvas);
}
function update() {
frames++;
if (keystate[KEY_LEFT] && snake.direction !== RIGHT)
snake.direction = LEFT;
if (keystate[KEY_UP] && snake.direction !== DOWN)
snake.direction = UP;
if (keystate[KEY_RIGHT] && snake.direction !== LEFT)
snake.direction = RIGHT;
if (keystate[KEY_DOWN] && snake.direction !== UP)
snake.direction = DOWN;
if (frames%5 === 0 ) {
var nx = snake.last.x;
var ny = snake.last.x;
switch (snake.direction) {
case LEFT:
nx--;
break;
case UP:
ny--;
break;
case RIGHT:
nx++;
break;
case DOWN:
ny++;
break;
}
if (nx < 0 || nx > grid.width-1 ||
ny < 0 || ny > grid.height-1 ||
grid.get(nx, ny) === SNAKE
) {
return init();
}
if (grid.get(nx, ny) === FRUIT) {
var tail = {x:nx, y:ny};
setFood();
} else {
var tail = snake.remove();
grid.set(EMPTY, tail.x, tail.y);
tail.x = nx;
tail.y = ny;
}
grid.set(SNAKE, tail.x, tail.y);
snake.insert(tail.x, tail.y);
}
}
function draw() {
var tw = canvas.width/grid.width;
var th = canvas.height/grid.height;
for (var x = 0; x < grid.width; x++) {
for (var y = 0; y < grid.height; y++) {
switch (grid.get(x, y)) {
case EMPTY:
ctx.fillStyle = "#fff";
break;
case SNAKE:
ctx.fillStyle = "#0ff";
break;
case FRUIT:
ctx.fillStyle = "#f00";
break;
}
ctx.fillRect(x*tw, y*th, y*th, tw, th);
}
}
}
main();
Alright, I found part of your problem:
if (nx < 0 || nx > grid.width-1 ||
ny < 0 || ny > grid.height-1 ||
grid.get(nx, ny) === SNAKE
) {
return init();
This constantly calls init over and over which restores your snake to the default position.
Removing this line and your snake stays still until movement starts.
It still pulls down and two the right -- which hints to me that you want to check the direction handling code. Use either the debugging tools built into the browser (or console.log) to ensure those values are what you think they are.

Categories