I'm currently troubleshooting an issue with my snake game where the tail array of the snake is only being drawn one block at a time after eating the food. My goal is to have the entire tail array drawn together and smoothly follow the snake head, creating a complete snake appearance. However, when the snake head moves over the food, only one rectangle is drawn in the spot where the food was located, and the head continues to be drawn in its new position.
It seems like the tail array of the snake is getting drawn throughout the map individually instead of as a connected segment.
Below you can find the code I am working with:
Thank you for any assistance!
Entire Code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">>
<title>Document</title>
<style>
.new-div {
display:flex;
justify-content: center;
align-items: center;
width:400px;
height:400px;
position:absolute;
top:0;
z-index:4000;
}
.game-content {
position:relative;
top:35px;
right:0px;
border:none;
border-radius:5px;
padding:10px;
background-color:teal;
color:white;
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="400" style="border:1px solid black"></canvas>
<script type="text/javascript">
window.onload = function() {
fruit.generateNewFruit();
window.requestAnimationFrame(main);
}
var tools = {
canvas: document.getElementById('canvas'),
ctx: canvas.getContext('2d'),
drawRect: function(x, y, sizeX, sizeY, color, type, stroke, strokeColor) {
if (type == 'fill') {
this.ctx.fillStyle = color;
this.ctx.fillRect(x, y, sizeX, sizeY);
if (stroke == true) {
this.ctx.strokeStyle = strokeColor;
this.ctx.strokeRect(x, y, sizeX, sizeY);
}
} else {
this.ctx.fillStyle = color;
this.ctx.strokeRect(x, y, sizeX, sizeY);
}
},
writeText: function(text, x, y, font, fontStyle, color) {
if (font) {
this.ctx.font = fontStyle;
}
this.ctx.fillStyle = color;
this.ctx.fillText(text, x, y);
}
}
let game = {
x: tools.canvas.width / 2 - 40,
y: tools.canvas.height / 2,
gameOver: false,
gameOverMsg: function(msg, font) {
let newDiv = document.createElement('div');
let button = document.createElement('button');
let btnText = document.createTextNode('Play Again');
button.className = "game-content";
newDiv.className = "new-div";
tools.writeText(msg, this.x, this.y, true, "16px bold sans-serif", "#fff");
button.appendChild(btnText);
newDiv.appendChild(button);
document.body.appendChild(newDiv);
document.getElementsByClassName('game-content')[0].addEventListener('click', function() {
game.gameOver = true;
});
}
}
let map = {
tileX: 0,
tileY: 0,
tileRow: 20,
tileCol: 20,
tileSize: 20,
tileColor: 'gray',
drawMap: function() {
for (let col = 0; col < this.tileCol; col++) {
for (let row = 0; row < this.tileRow; row++) {
tools.drawRect(this.tileX, this.tileY, this.tileSize, this.tileSize, this.tileColor, 'fill');
this.tileX += this.tileSize;
}
this.tileX = 0;
this.tileY += this.tileSize;
}
this.tileY = 0;
},
outOfBounds: function() {
if (snake.x < 0 || snake.x > tools.canvas.width || snake.y < 0 || snake.y > tools.canvas.height) {
game.gameOver = true;
}
}
}
let fruit = {
x: 0,
y: 0,
size: 20,
fillColor: 'hotpink',
strokeColor: 'black',
drawFruit: function() {
tools.drawRect(this.x, this.y, this.size, this.size, this.fillColor, 'fill', true, this.strokeColor);
},
generateNewFruit: function() {
this.x = Math.floor(Math.random() * 20) * 20;
this.y = Math.floor(Math.random() * 20) * 20;
}
}
let snake = {
x: 0,
y: 0,
size: 20,
color: 'black',
direction: '',
bodySize: 0,
init: false,
tail: [],
drawSnake: function() {
for (let i=0; i < this.bodySize; i++) {
tools.drawRect(this.tail[i].x, this.tail[i].y, this.size, this.size, this.color, 'fill', true, 'lime');
}
tools.drawRect(this.x, this.y, this.size, this.size, this.color, 'fill', true, 'lime');
},
move: function() {
if (this.direction == 'left') {
this.x-=this.size;
}
else if (this.direction == 'up') {
this.y-=this.size;
}
else if (this.direction == 'right') {
this.x+=this.size;
}
else if (this.direction == 'down') {
this.y+=this.size;
}
}
}
let drawEverything = function() {
if (game.gameOver) {
window.cancelAnimationFrame(main);
}
if (snake.x === fruit.x && snake.y === fruit.y) {
fruit.generateNewFruit();
snake.bodySize++;
if (snake.bodySize === snake.tail.length) {
for (let i=0; i < snake.tail.length - 1; i++) {
snake.tail[i] = snake.tail[i+1];
}
}
snake.tail[snake.bodySize-1] = {x: snake.x, y: snake.y};
}
tools.ctx.clearRect(0, 0, tools.canvas.width, tools.canvas.height);
map.drawMap();
map.outOfBounds();
snake.drawSnake();
snake.move();
fruit.drawFruit();
}
let main = function() {
if (game.gameOver) {
game.gameOverMsg("Game Over");
cancelAnimationFrame(main);
return;
} else {
drawEverything();
setTimeout(function() {
requestAnimationFrame(main);
}, 1000/20);
}
}
window.addEventListener('keydown', function(e) {
let key = e.keyCode;
switch(key) {
case 65: snake.direction = 'left';
break;
case 87: snake.direction = 'up';
break;
case 68: snake.direction = 'right';
break;
case 83: snake.direction = 'down';
break;
}
});
</script>
</body>
</html>
This section handles shifting the snake downward:
let drawEverything = function() {
if (game.gameOver) {
window.cancelAnimationFrame(main);
}
if (snake.x === fruit.x && snake.y === fruit.y) {
fruit.generateNewFruit();
snake.bodySize++;
if (snake.bodySize === snake.tail.length) {
for (let i=0; i < snake.tail.length - 1; i++) {
snake.tail[i] = snake.tail[i+1];
}
}
snake.tail[snake.bodySize-1] = {x: snake.x, y: snake.y};
}
tools.ctx.clearRect(0, 0, tools.canvas.width, tools.canvas.height);
map.drawMap();
map.outOfBounds();
snake.drawSnake();
snake.move();
fruit.drawFruit();
}
Here is the part responsible for drawing the updated shifted-down tail and snake head:
drawSnake: function() {
for (let i=0; i < this.bodySize; i++) {
tools.drawRect(this.tail[i].x, this.tail[i].y, this.size, this.size, this.color, 'fill', true, 'lime');
}
tools.drawRect(this.x, this.y, this.size, this.size, this.color, 'fill', true, 'lime');
}
If you have insights on why the tail array is not being drawn as a continuous set of blocks or if there's an issue with the drawSnake function, your help would be greatly appreciated.