贪吃蛇游戏

游戏说明

  1. 点击”开始游戏”按钮开始
  2. 使用键盘方向键控制蛇的移动方向
  3. 吃到红色食物可以得分,速度会逐渐加快
  4. 撞到墙壁或自己的身体游戏结束
  5. 可以随时暂停/继续游戏
  6. canvas上面最好不要加width和height,用css控制能够更好的动态控制宽高

技术实现

这个游戏主要使用了以下技术:

  • HTML5 Canvas:用于游戏画面的渲染
  • JavaScript:实现游戏逻辑
  • CSS:页面样式和布局

游戏的核心功能包括:

  1. 蛇的移动控制
  2. 碰撞检测
  3. 食物生成
  4. 速度控制
  5. 游戏状态管理

代码详解

让我们详细解释游戏的核心代码实现:

1. 基础设置和变量

1
2
3
4
5
const canvas = document.getElementById('gameCanvas');
const box = 20; // 每个网格的大小
let drawInterval = null; // 游戏循环的计时器
let defaultSpeed = 500; // 游戏初始速度(毫秒)
let drawProcess = false; // 防止重复绘制的标志

这些是游戏的基本配置。我们使用 20x20 像素的网格作为基本单位,defaultSpeed 控制游戏速度,初始设置为 500 毫秒更新一次。

2. Canvas 初始化

1
2
3
4
5
6
function initCanvas() {
canvas.width = canvas.offsetWidth;
canvas.height = 400;
gridWidth = Math.floor(canvas.width / box);
gridHeight = Math.floor(canvas.height / box);
}

这个函数负责初始化 Canvas 的尺寸。它会:

  • 根据容器宽度设置 Canvas 的实际像素宽度
  • 设置固定的高度(400像素)
  • 计算网格的行数和列数

3. 蛇和食物的数据结构

  • 这里蛇需要理解,上下左右给蛇加个新头,然后去掉尾巴,就能够完成蛇的移动了,这是个关键因素。
1
2
3
let snake = [{ x: Math.floor(gridWidth / 2) * box, y: Math.floor(gridHeight / 2) * box }];
let food;
let direction = null;
  • 蛇被表示为一个对象数组,每个对象包含 x 和 y 坐标
  • 初始时蛇只有一个节点,位于画布中心
  • direction 表示蛇的移动方向(UP、DOWN、LEFT、RIGHT)

4. 食物生成

1
2
3
4
5
6
7
8
9
function generateFood() {
do {
food = {
x: Math.floor(Math.random() * gridWidth) * box,
y: Math.floor(Math.random() * gridHeight) * box
};
} while (snake.some(seg => seg.x === food.x && seg.y === food.y) &&
isPointWithinBoundary(food.x, food.y, 50));
}

这个函数负责在随机位置生成食物:

  • 使用 do-while 循环确保食物不会生成在蛇身上
  • isPointWithinBoundary 函数确保食物不会太靠近边界

5. 游戏主循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
function draw() {
if (drawProcess) return;
drawProcess = true;

// 清空画布
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);

// 绘制食物
ctx.fillStyle = 'red';
ctx.fillRect(food.x, food.y, box, box);

// 绘制蛇
for (let i = 0; i < snake.length; i++) {
ctx.fillStyle = i === 0 ? 'green' : 'lightgreen';
ctx.fillRect(snake[i].x, snake[i].y, box, box);
}

// 移动蛇
if (direction) {
let head = { ...snake[0] };

// 根据方向更新蛇头位置
if (direction === 'UP') head.y -= box;
if (direction === 'DOWN') head.y += box;
if (direction === 'LEFT') head.x -= box;
if (direction === 'RIGHT') head.x += box;

// 碰撞检测
if (head.x < 0 || head.x >= canvas.width ||
head.y < 0 || head.y >= canvas.height ||
snake.some(seg => seg.x === head.x && seg.y === head.y)) {
clearInterval(drawInterval);
alert('Game Over!');
restartGame();
drawProcess = false;
return;
}

// 检查是否吃到食物
if (head.x === food.x && head.y === food.y) {
defaultSpeed = Math.max(100, defaultSpeed - 50); // 加快速度
generateFood();
startDraw();
} else {
snake.pop(); // 如果没吃到食物,移除尾部
}

snake.unshift(head); // 添加新的头部
}
drawProcess = false;
}

游戏主循环是最复杂的部分,它负责:

  1. 清空并重绘画布
  2. 绘制食物和蛇
  3. 根据方向移动蛇
  4. 处理碰撞检测
  5. 处理食物收集
  6. 控制游戏速度

6. 游戏控制函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function startDraw() {
if (drawInterval) clearInterval(drawInterval);
drawInterval = setInterval(draw, defaultSpeed);
}

function restartGame() {
clearInterval(drawInterval);
defaultSpeed = 500;
snake = [{ x: Math.floor(gridWidth / 2) * box, y: Math.floor(gridHeight / 2) * box }];
generateFood();
direction = null;
startDraw();
}

function pauseGame() {
if (drawInterval) {
clearInterval(drawInterval);
drawInterval = null;
} else {
startDraw();
}
}

function startGame() {
if (!direction) {
direction = 'LEFT'; // 设置初始方向
}
if (!drawInterval) {
startDraw();
}
}

这些函数处理游戏的各种状态:

  • startDraw: 启动游戏循环
  • restartGame: 重置游戏状态
  • pauseGame: 暂停/继续游戏
  • startGame: 开始新游戏

7. 事件监听

1
2
3
4
5
6
7
8
9
10
11
window.addEventListener('resize', () => {
initCanvas();
restartGame();
});

document.addEventListener('keydown', event => {
if (event.key === 'ArrowUp' && direction !== 'DOWN') direction = 'UP';
if (event.key === 'ArrowDown' && direction !== 'UP') direction = 'DOWN';
if (event.key === 'ArrowLeft' && direction !== 'RIGHT') direction = 'LEFT';
if (event.key === 'ArrowRight' && direction !== 'LEFT') direction = 'RIGHT';
});

事件监听器处理:

  • 窗口大小改变时重置游戏
  • 键盘输入控制蛇的方向(确保蛇不能直接掉头)