Creating a simple Snake game is a fantastic project to deepen your understanding of JavaScript, HTML, and CSS. In this blog, we’ll guide you through building a Snake game from scratch using vanilla JavaScript. By the end, you'll have a fully functional game where the snake grows as it eats food and ends when it collides with itself or the walls.
What Will You Learn?
- Setting up an HTML canvas for game rendering.
- Handling keyboard input to control the snake.
- Implementing basic game mechanics: movement, food generation, and collision detection.
- Using JavaScript’s
setInterval
function for game loops.
Step 1: Setting Up the Project
HTML Structure
Start by creating a basic HTML file with a canvas
element. The canvas
will serve as the game board.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snake Game</title>
<style>
canvas {
border: 2px solid black;
display: block;
margin: 20px auto;
}
body {
text-align: center;
font-family: Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Snake Game</h1>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<script src="snake.js"></script>
</body>
</html>
Step 2: Initializing the Canvas
In the snake.js
file, we’ll initialize the canvas and context for rendering.
// Select the canvas and set up the 2D drawing context
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Set the size of each grid cell
const gridSize = 20;
// Initialize the game variables
let snake = [{ x: gridSize * 5, y: gridSize * 5 }];
let direction = { x: 0, y: 0 };
let food = generateFood();
let score = 0;
Step 3: Rendering the Snake and Food
Define functions to draw the snake and the food on the canvas.
Drawing the Snake
We’ll loop through the snake
array and draw each segment as a square.
function drawSnake() {
ctx.fillStyle = 'green';
snake.forEach(segment => {
ctx.fillRect(segment.x, segment.y, gridSize, gridSize);
});
}
Drawing the Food
The food is a red square randomly placed on the grid.
function drawFood() {
ctx.fillStyle = 'red';
ctx.fillRect(food.x, food.y, gridSize, gridSize);
}
Step 4: Game Mechanics
Snake Movement
The snake moves by adding a new head in the direction of movement and removing the tail.
function moveSnake() {
const newHead = {
x: snake[0].x + direction.x * gridSize,
y: snake[0].y + direction.y * gridSize,
};
snake.unshift(newHead); // Add the new head
// If the snake eats food, keep the tail (do not pop)
if (newHead.x === food.x && newHead.y === food.y) {
score++;
food = generateFood(); // Generate new food
} else {
snake.pop(); // Remove the tail
}
}
Food Generation
Ensure the food doesn’t appear inside the snake.
function generateFood() {
let foodX, foodY;
do {
foodX = Math.floor(Math.random() * (canvas.width / gridSize)) * gridSize;
foodY = Math.floor(Math.random() * (canvas.height / gridSize)) * gridSize;
} while (snake.some(segment => segment.x === foodX && segment.y === foodY));
return { x: foodX, y: foodY };
}
Step 5: Collision Detection
Wall Collision
Check if the snake’s head is out of bounds.
function checkWallCollision() {
const head = snake[0];
return (
head.x < 0 || head.x >= canvas.width || head.y < 0 || head.y >= canvas.height
);
}
Self-Collision
Check if the snake’s head touches any part of its body.
function checkSelfCollision() {
const head = snake[0];
return snake.slice(1).some(segment => segment.x === head.x && segment.y === head.y);
}
Step 6: Handling User Input
Add an event listener to track the arrow keys and update the snake's direction.
document.addEventListener('keydown', event => {
const { key } = event;
if (key === 'ArrowUp' && direction.y === 0) {
direction = { x: 0, y: -1 };
} else if (key === 'ArrowDown' && direction.y === 0) {
direction = { x: 0, y: 1 };
} else if (key === 'ArrowLeft' && direction.x === 0) {
direction = { x: -1, y: 0 };
} else if (key === 'ArrowRight' && direction.x === 0) {
direction = { x: 1, y: 0 };
}
});
Step 7: Game Loop
The game loop updates the game state and re-renders everything.
function gameLoop() {
if (checkWallCollision() || checkSelfCollision()) {
alert(`Game Over! Your score: ${score}`);
document.location.reload(); // Reload the page to restart the game
}
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
drawSnake();
drawFood();
moveSnake();
}
// Run the game loop every 100ms
setInterval(gameLoop, 100);
Final Code
Here’s the complete code in one place for easy reference:
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const gridSize = 20;
let snake = [{ x: gridSize * 5, y: gridSize * 5 }];
let direction = { x: 0, y: 0 };
let food = generateFood();
let score = 0;
function drawSnake() {
ctx.fillStyle = 'green';
snake.forEach(segment => ctx.fillRect(segment.x, segment.y, gridSize, gridSize));
}
function drawFood() {
ctx.fillStyle = 'red';
ctx.fillRect(food.x, food.y, gridSize, gridSize);
}
function moveSnake() {
const newHead = {
x: snake[0].x + direction.x * gridSize,
y: snake[0].y + direction.y * gridSize,
};
snake.unshift(newHead);
if (newHead.x === food.x && newHead.y === food.y) {
score++;
food = generateFood();
} else {
snake.pop();
}
}
function generateFood() {
let foodX, foodY;
do {
foodX = Math.floor(Math.random() * (canvas.width / gridSize)) * gridSize;
foodY = Math.floor(Math.random() * (canvas.height / gridSize)) * gridSize;
} while (snake.some(segment => segment.x === foodX && segment.y === foodY));
return { x: foodX, y: foodY };
}
function checkWallCollision() {
const head = snake[0];
return head.x < 0 || head.x >= canvas.width || head.y < 0 || head.y >= canvas.height;
}
function checkSelfCollision() {
const head = snake[0];
return snake.slice(1).some(segment => segment.x === head.x && segment.y === head.y);
}
document.addEventListener('keydown', event => {
const { key } = event;
if (key === 'ArrowUp' && direction.y === 0) direction = { x: 0, y: -1 };
else if (key === 'ArrowDown' && direction.y === 0) direction = { x: 0, y: 1 };
else if (key === 'ArrowLeft' && direction.x === 0) direction = { x: -1, y: 0 };
else if (key === 'ArrowRight' && direction.x === 0) direction = { x: 1, y: 0 };
});
function gameLoop() {
if (checkWallCollision() || checkSelfCollision()) {
alert(`Game Over! Your score: ${score}`);
document.location.reload();
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawSnake();
drawFood();
moveSnake();
}
setInterval(gameLoop, 100);
Conclusion
Congratulations! You’ve built a fully functional Snake game in vanilla JavaScript. You can now experiment further by adding features such as:
- Increasing the game’s speed as the snake grows.
- Keeping a high-score record.
- Adding sound effects or themes.
Happy coding!
Comments
Post a Comment