174 lines
5.6 KiB
JavaScript
174 lines
5.6 KiB
JavaScript
const canvas = document.getElementById('snakeCanvas');
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
const gridSize = 20;
|
|
const tileSize = 100;
|
|
const snakeSize = 60;
|
|
const foodSize = 80;
|
|
canvas.width = gridSize * tileSize;
|
|
canvas.height = gridSize * tileSize;
|
|
|
|
let snake = [{ x: 10, y: 10 }, { x: 10, y: 11 }, { x: 10, y: 12 }];
|
|
let direction = { x: 0, y: 0 };
|
|
let food = { x: Math.floor(Math.random() * gridSize), y: Math.floor(Math.random() * gridSize) };
|
|
let score = 0;
|
|
let gameOver = false;
|
|
|
|
function draw() {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// draw grid of checkerboard pattern
|
|
for (let x = 0; x < gridSize; x++) {
|
|
for (let y = 0; y < gridSize; y++) {
|
|
ctx.fillStyle = (x + y) % 2 === 0 ?
|
|
getComputedStyle(document.documentElement).getPropertyValue('--background-color') :
|
|
getComputedStyle(document.documentElement).getPropertyValue('--secondary-background-color');
|
|
ctx.fillRect(x * tileSize, y * tileSize, tileSize, tileSize);
|
|
}
|
|
}
|
|
|
|
// Draw snake
|
|
snake.forEach(segment => {
|
|
let nextVec = { x: 0, y: 0 };
|
|
// if there is a segment after the current segment
|
|
if (snake.indexOf(segment) < snake.length - 1) {
|
|
const nextSegment = snake[snake.indexOf(segment) + 1];
|
|
nextVec.x = nextSegment.x - segment.x;
|
|
nextVec.y = nextSegment.y - segment.y;
|
|
}
|
|
|
|
ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--primary-color');
|
|
if (nextVec.x === 0 && nextVec.y === 0) {
|
|
ctx.fillRect(
|
|
segment.x * tileSize + (tileSize - snakeSize) / 2,
|
|
segment.y * tileSize + (tileSize - snakeSize) / 2,
|
|
snakeSize,
|
|
snakeSize
|
|
);
|
|
} else if (nextVec.x > 0 || nextVec.y > 0) {
|
|
ctx.fillRect(
|
|
segment.x * tileSize + (tileSize - snakeSize) / 2,
|
|
segment.y * tileSize + (tileSize - snakeSize) / 2,
|
|
snakeSize + nextVec.x * (tileSize - snakeSize),
|
|
snakeSize + nextVec.y * (tileSize - snakeSize)
|
|
);
|
|
} else {
|
|
ctx.fillRect(
|
|
segment.x * tileSize + (tileSize - snakeSize) / 2 + nextVec.x * (tileSize - snakeSize),
|
|
segment.y * tileSize + (tileSize - snakeSize) / 2 + nextVec.y * (tileSize - snakeSize),
|
|
snakeSize + Math.abs(nextVec.x) * (tileSize - snakeSize),
|
|
snakeSize + Math.abs(nextVec.y) * (tileSize - snakeSize)
|
|
);
|
|
}
|
|
|
|
});
|
|
|
|
// Draw food
|
|
ctx.fillStyle = '#ff4d4d';
|
|
ctx.fillRect(
|
|
food.x * tileSize + (tileSize - foodSize) / 2,
|
|
food.y * tileSize + (tileSize - foodSize) / 2,
|
|
foodSize,
|
|
foodSize
|
|
);
|
|
}
|
|
|
|
function update() {
|
|
if (gameOver) return;
|
|
|
|
// Move snake
|
|
const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y };
|
|
|
|
// Add new head
|
|
snake.unshift(head);
|
|
|
|
// Check for food collision
|
|
if (head.x === food.x && head.y === food.y) {
|
|
score += 10; // Increase score
|
|
placeFood();
|
|
} else {
|
|
snake.pop(); // Remove tail if no food eaten
|
|
}
|
|
|
|
// Check for wall collision
|
|
if (head.x < 0 || head.x >= gridSize || head.y < 0 || head.y >= gridSize) {
|
|
gameOver = true;
|
|
return;
|
|
}
|
|
|
|
// Check for self collision
|
|
for (let i = 1; i < snake.length; i++) {
|
|
if (head.x === snake[i].x && head.y === snake[i].y) {
|
|
gameOver = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
function placeFood() {
|
|
do {
|
|
food.x = Math.floor(Math.random() * gridSize);
|
|
food.y = Math.floor(Math.random() * gridSize);
|
|
} while (snake.some(segment => segment.x === food.x && segment.y === food.y));
|
|
}
|
|
|
|
function changeDirection(event) {
|
|
switch (event.key) {
|
|
case 'w':
|
|
if (direction.y === 0) direction = { x: 0, y: -1 };
|
|
break;
|
|
case 's':
|
|
if (direction.y === 0) direction = { x: 0, y: 1 };
|
|
break;
|
|
case 'a':
|
|
if (direction.x === 0) direction = { x: -1, y: 0 };
|
|
break;
|
|
case 'd':
|
|
if (direction.x === 0) direction = { x: 1, y: 0 };
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Menu to start the game
|
|
function menu() {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--background-color');
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
ctx.textAlign = 'center';
|
|
ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--text-color');
|
|
ctx.font = '200px Arial';
|
|
ctx.fillText('Snake Game', canvas.width / 2, canvas.height / 2);
|
|
ctx.font = '100px Arial';
|
|
ctx.fillText('Press W/A/S/D to move', canvas.width / 2, canvas.height / 2 + 100);
|
|
ctx.fillText('Click to start', canvas.width / 2, canvas.height / 2 + 200);
|
|
|
|
canvas.addEventListener('click', startGame);
|
|
}
|
|
|
|
function gameLoop() {
|
|
if (!gameOver) {
|
|
update();
|
|
draw();
|
|
setTimeout(gameLoop, 100);
|
|
} else {
|
|
document.removeEventListener('keydown', changeDirection);
|
|
document.getElementById('snake-score').value = score;
|
|
alert(`Game Over! Your score: ${score}`);
|
|
menu();
|
|
}
|
|
}
|
|
|
|
function startGame() {
|
|
snake = [{ x: 10, y: 10 }, { x: 10, y: 11 }, { x: 10, y: 12 }];
|
|
direction = { x: 1, y: 0 };
|
|
food = { x: Math.floor(Math.random() * gridSize), y: Math.floor(Math.random() * gridSize) };
|
|
score = 0;
|
|
gameOver = false;
|
|
canvas.removeEventListener('click', startGame);
|
|
document.addEventListener('keydown', changeDirection);
|
|
gameLoop();
|
|
}
|
|
|
|
menu(); |