- 游戏概述:我们想要实现什么。
- HTML 结构:游戏画布和基本布局。
- CSS 样式:让游戏看起来更美观。
- JavaScript 游戏逻辑:核心代码,分步讲解。
- 完整代码:将所有部分整合在一起。
游戏概述
我们的贪吃蛇游戏将包含以下功能:

(图片来源网络,侵删)
- 一个网格画布,蛇和食物在其中移动。
- 玩家通过键盘方向键(或 WASD)控制蛇的移动方向。
- 蛇吃到食物后,身体会变长,并且得分增加。
- 如果蛇撞到墙壁或自己的身体,游戏结束,并显示最终得分。
HTML 结构
我们需要一个 HTML 文件来放置游戏的画布(Canvas)和显示分数的元素。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">贪吃蛇游戏</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="game-container">
<h1>贪吃蛇</h1>
<div class="score-container">
<span>得分: <span id="score">0</span></span>
</div>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<div id="gameOver" class="hidden">
<h2>游戏结束!</h2>
<p>最终得分: <span id="finalScore">0</span></p>
<button id="restartButton">重新开始</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
解释:
<canvas id="gameCanvas">: 这是我们的游戏画布,所有图形都将在这里绘制,我们设置了宽度和高度为 400px。<span id="score">: 用于实时显示当前得分。<div id="gameOver">: 游戏结束时显示的界面,默认是隐藏的 (hidden类)。<script src="script.js">: 引入我们的 JavaScript 文件。
CSS 样式
创建一个 style.css 文件来美化我们的游戏。
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
font-family: Arial, sans-serif;
}
.game-container {
text-align: center;
}
h1 {
color: #333;
}
.score-container {
margin: 10px 0;
font-size: 1.2em;
font-weight: bold;
}
#gameCanvas {
background-color: #111;
border: 2px solid #333;
display: block; /* 移除 canvas 底部的小间隙 */
}
#gameOver {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.8);
color: white;
padding: 20px;
border-radius: 10px;
text-align: center;
}
#gameOver h2 {
margin-top: 0;
}
#restartButton {
margin-top: 15px;
padding: 10px 20px;
font-size: 1em;
cursor: pointer;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
}
#restartButton:hover {
background-color: #45a049;
}
.hidden {
display: none;
}
JavaScript 游戏逻辑
这是最核心的部分,我们将创建一个 script.js 文件,并逐步构建游戏逻辑。

(图片来源网络,侵删)
步骤 1: 初始化和变量定义
// 1. 获取画布和上下文
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const finalScoreElement = document.getElementById('finalScore');
const gameOverElement = document.getElementById('gameOver');
const restartButton = document.getElementById('restartButton');
// 2. 游戏变量
const gridSize = 20; // 每个格子的大小
const tileCount = canvas.width / gridSize; // 网格数量 (20x20)
let snake = [
{x: 10, y: 10} // 蛇的初始位置,用一个数组表示身体
];
let food = {}; // 食物的位置
let dx = 0; // 蛇在 x 轴上的移动速度 (0表示静止)
let dy = 0; // 蛇在 y 轴上的移动速度
let score = 0;
let gameRunning = true;
步骤 2: 生成食物
我们需要一个函数来随机生成食物的位置,并且不能生成在蛇的身体上。
function generateFood() {
food = {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
// 简单起见,我们不检查食物是否生成在蛇身上,因为概率很低
// 更健壮的做法是循环检查,直到生成一个不与蛇身重叠的位置
}
步骤 3: 绘制游戏元素
我们需要绘制蛇和食物。
function drawGame() {
// 清空画布
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制蛇
ctx.fillStyle = 'lime';
snake.forEach(segment => {
ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize - 2, gridSize - 2);
});
// 绘制食物
ctx.fillStyle = 'red';
ctx.fillRect(food.x * gridSize, food.y * gridSize, gridSize - 2, gridSize - 2);
}
步骤 4: 移动蛇和碰撞检测
这是游戏的核心逻辑。
function moveSnake() {
if (!gameRunning) return;
const head = {x: snake[0].x + dx, y: snake[0].y + dy};
// 检查是否撞墙
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) {
gameOver();
return;
}
// 检查是否撞到自己
for (let segment of snake) {
if (head.x === segment.x && head.y === segment.y) {
gameOver();
return;
}
}
snake.unshift(head); // 将新的头部添加到蛇身体数组的前面
// 检查是否吃到食物
if (head.x === food.x && head.y === food.y) {
score += 10;
scoreElement.textContent = score;
generateFood(); // 生成新的食物
} else {
snake.pop(); // 如果没吃到食物,移除蛇的尾部
}
}
步骤 5: 游戏循环
使用 setInterval 来控制游戏的节奏。

(图片来源网络,侵删)
function gameLoop() {
moveSnake();
drawGame();
}
// 初始化游戏
generateFood();
drawGame();
// 每 100毫秒 执行一次游戏循环,控制游戏速度
setInterval(gameLoop, 100);
步骤 6: 键盘控制
监听键盘事件来改变蛇的移动方向。
document.addEventListener('keydown', (e) => {
if (!gameRunning) return;
// 防止蛇直接掉头
if (e.key === 'ArrowUp' && dy === 0) {
dx = 0;
dy = -1;
} else if (e.key === 'ArrowDown' && dy === 0) {
dx = 0;
dy = 1;
} else if (e.key === 'ArrowLeft' && dx === 0) {
dx = -1;
dy = 0;
} else if (e.key === 'ArrowRight' && dx === 0) {
dx = 1;
dy = 0;
}
// 你也可以添加 WASD 控制
});
步骤 7: 游戏结束和重新开始
function gameOver() {
gameRunning = false;
finalScoreElement.textContent = score;
gameOverElement.classList.remove('hidden');
}
restartButton.addEventListener('click', () => {
// 重置所有游戏变量
snake = [{x: 10, y: 10}];
dx = 0;
dy = 0;
score = 0;
scoreElement.textContent = score;
gameRunning = true;
generateFood();
gameOverElement.classList.add('hidden');
});
完整代码
将以上所有 JavaScript 代码合并到 script.js 文件中。
script.js
// 1. 获取画布和上下文
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const finalScoreElement = document.getElementById('finalScore');
const gameOverElement = document.getElementById('gameOver');
const restartButton = document.getElementById('restartButton');
// 2. 游戏变量
const gridSize = 20; // 每个格子的大小
const tileCount = canvas.width / gridSize; // 网格数量 (20x20)
let snake = [
{x: 10, y: 10} // 蛇的初始位置,用一个数组表示身体
];
let food = {}; // 食物的位置
let dx = 0; // 蛇在 x 轴上的移动速度 (0表示静止)
let dy = 0; // 蛇在 y 轴上的移动速度
let score = 0;
let gameRunning = true;
// 生成食物
function generateFood() {
food = {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
}
// 绘制游戏
function drawGame() {
// 清空画布
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制蛇
ctx.fillStyle = 'lime';
snake.forEach(segment => {
ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize - 2, gridSize - 2);
});
// 绘制食物
ctx.fillStyle = 'red';
ctx.fillRect(food.x * gridSize, food.y * gridSize, gridSize - 2, gridSize - 2);
}
// 移动蛇和碰撞检测
function moveSnake() {
if (!gameRunning) return;
const head = {x: snake[0].x + dx, y: snake[0].y + dy};
// 检查是否撞墙
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) {
gameOver();
return;
}
// 检查是否撞到自己
for (let segment of snake) {
if (head.x === segment.x && head.y === segment.y) {
gameOver();
return;
}
}
snake.unshift(head); // 将新的头部添加到蛇身体数组的前面
// 检查是否吃到食物
if (head.x === food.x && head.y === food.y) {
score += 10;
scoreElement.textContent = score;
generateFood(); // 生成新的食物
} else {
snake.pop(); // 如果没吃到食物,移除蛇的尾部
}
}
// 游戏循环
function gameLoop() {
moveSnake();
drawGame();
}
// 键盘控制
document.addEventListener('keydown', (e) => {
if (!gameRunning) return;
// 防止蛇直接掉头
if (e.key === 'ArrowUp' && dy === 0) {
dx = 0;
dy = -1;
} else if (e.key === 'ArrowDown' && dy === 0) {
dx = 0;
dy = 1;
} else if (e.key === 'ArrowLeft' && dx === 0) {
dx = -1;
dy = 0;
} else if (e.key === 'ArrowRight' && dx === 0) {
dx = 1;
dy = 0;
}
});
// 游戏结束
function gameOver() {
gameRunning = false;
finalScoreElement.textContent = score;
gameOverElement.classList.remove('hidden');
}
// 重新开始
restartButton.addEventListener('click', () => {
snake = [{x: 10, y: 10}];
dx = 0;
dy = 0;
score = 0;
scoreElement.textContent = score;
gameRunning = true;
generateFood();
gameOverElement.classList.add('hidden');
});
// 初始化游戏
generateFood();
drawGame();
setInterval(gameLoop, 100);
如何运行
- 创建一个项目文件夹,
snake-game。 - 在该文件夹中创建三个文件:
index.html、style.css和script.js。 - 将上面提供的代码分别复制到对应的文件中。
- 用浏览器打开
index.html文件,即可开始游戏!
进一步的改进方向
这个贪吃蛇游戏已经具备了基本功能,你还可以尝试以下改进:
- 难度选择:增加速度选择(Easy, Medium, Hard)。
- 音效:添加吃食物和游戏结束的音效。
- 最高分记录:使用
localStorage来保存和显示历史最高分。 - 更复杂的食物:例如不同颜色的食物有不同效果(加分、减速、变长等)。
- 优化食物生成:确保食物不会生成在蛇身上。
希望这个详细的教程能帮助你理解如何用 JavaScript 编写一个网页游戏!
