文章目录
手把手教你用HTML5 Canvas开发贪吃蛇(Snake)游戏
贪吃蛇是经典的小游戏,通过方向键控制蛇移动吃食物,蛇身会随食物增加而变长,撞到边界或自身则游戏结束。本文将使用HTML5 Canvas和原生JavaScript,从零开始实现这个游戏,适合编程初学者学习。

一、开发前的准备知识
在开始前,我们需要了解几个核心技术点:
- HTML5 Canvas:用于绘制游戏画面的画布元素
- JavaScript 基础:数组操作、事件处理、定时器
- 游戏循环:通过定时器不断更新游戏状态和画面
准备工作:创建一个HTML文件(例如snake-game.html),我们将所有代码写在这个文件中。
二、步骤1:搭建基础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>贪吃蛇游戏</title>
<style>
/* 简单样式美化 */
body {
display: flex;
flex-direction: column;
align-items: center;
background-color: #f0f0f0;
font-family: Arial, sans-serif;
}
#gameCanvas {
border: 3px solid #333;
background-color: #fff;
}
.info {
margin: 10px 0;
font-size: 1.2em;
}
</style>
</head>
<body>
<h1>贪吃蛇游戏</h1>
<div class="info">使用方向键控制蛇移动</div>
<!-- 游戏画布 -->
<canvas id="gameCanvas" width="400" height="400"></canvas>
<script>
// 游戏代码将写在这里
</script>
</body>
</html>
代码说明:
- 创建了一个400x400像素的Canvas元素,这是我们的游戏舞台
- 添加了简单的CSS样式,使页面居中显示并美化
- 预留了
<script>标签,用于编写游戏逻辑
三、步骤2:初始化游戏环境
接下来,我们需要获取Canvas上下文并定义游戏的核心变量。在<script>标签中添加以下代码:
// 获取Canvas元素和绘图上下文
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// 游戏核心参数
const gridSize = 20; // 网格大小(每个格子的像素数)
const gridWidth = canvas.width / gridSize; // 横向格子数
const gridHeight = canvas.height / gridSize; // 纵向格子数
// 蛇的初始状态(用数组存储蛇身的每个 segment)
let snake = [
{ x: 10, y: 10 } // 初始位置(网格坐标)
];
// 食物位置
let food = { x: 15, y: 15 };
// 蛇的移动方向(dx: 水平方向,dy: 垂直方向)
let dx = 1; // 初始向右移动
let dy = 0;
// 游戏状态
let gameOver = false;
let score = 0;
代码说明:
gridSize:将画布划分为20x20的网格,方便我们用格子坐标而非像素坐标来定位snake:数组存储蛇的每个身体段,初始只有一个头部dx和dy:控制移动方向(1表示正向,-1表示反向,0表示不移动)- 为什么用网格坐标?简化计算,避免直接操作像素,让蛇的移动更规整
四、步骤3:绘制游戏元素
我们需要编写函数来绘制蛇、食物和游戏状态。在已有代码后添加:
// 绘制蛇
function drawSnake() {
snake.forEach(segment => {
// 设置蛇的颜色
ctx.fillStyle = 'green';
// 绘制蛇的身体段(-1是为了显示网格线)
ctx.fillRect(
segment.x * gridSize, // 转换为像素坐标
segment.y * gridSize,
gridSize - 1,
gridSize - 1
);
});
}
// 绘制食物
function drawFood() {
ctx.fillStyle = 'red';
ctx.fillRect(
food.x * gridSize,
food.y * gridSize,
gridSize - 1,
gridSize - 1
);
}
// 绘制分数
function drawScore() {
ctx.fillStyle = '#333';
ctx.font = '16px Arial';
ctx.fillText(`分数: ${score}`, 10, 20);
}
// 绘制游戏结束画面
function drawGameOver() {
ctx.fillStyle = 'red';
ctx.font = '30px Arial';
ctx.textAlign = 'center';
ctx.fillText('游戏结束!', canvas.width / 2, canvas.height / 2);
ctx.font = '20px Arial';
ctx.fillText(`最终分数: ${score}`, canvas.width / 2, canvas.height / 2 + 30);
ctx.fillText('按F5重新开始', canvas.width / 2, canvas.height / 2 + 60);
}
代码说明:
fillRect(x, y, width, height):Canvas的矩形绘制方法- 蛇和食物的坐标都通过
gridSize转换为像素坐标 - 蛇的身体段使用
gridSize - 1绘制,留出1px间隙形成网格效果 - 游戏结束画面居中显示,提示玩家重新开始
五、步骤4:处理用户输入(方向控制)
贪吃蛇需要通过方向键控制移动方向,添加键盘事件监听:
// 处理键盘输入
document.addEventListener('keydown', changeDirection);
function changeDirection(event) {
// 阻止按键导致的页面滚动
event.preventDefault();
// 定义按键对应的方向(键码)
const LEFT_KEY = 37;
const RIGHT_KEY = 39;
const UP_KEY = 38;
const DOWN_KEY = 40;
// 获取当前按键
const keyPressed = event.keyCode;
// 防止反向移动(例如正在向右移动时不能直接向左)
const goingUp = dy === -1;
const goingDown = dy === 1;
const goingRight = dx === 1;
const goingLeft = dx === -1;
// 根据按键改变方向
if (keyPressed === LEFT_KEY && !goingRight) {
dx = -1;
dy = 0;
}
if (keyPressed === UP_KEY && !goingDown) {
dx = 0;
dy = -1;
}
if (keyPressed === RIGHT_KEY && !goingLeft) {
dx = 1;
dy = 0;
}
if (keyPressed === DOWN_KEY && !goingUp) {
dx = 0;
dy = 1;
}
}
代码说明:
- 使用
keydown事件监听键盘输入 - 通过键码判断用户按下的是哪个方向键
- 核心逻辑:防止蛇直接反向移动(例如向右移动时不能直接向左转),这是贪吃蛇游戏的基本规则
event.preventDefault():阻止方向键默认的页面滚动行为
六、步骤5:实现食物随机生成
当蛇吃到食物后,需要生成新的食物,且不能生成在蛇身上:
// 随机生成食物
function generateFood() {
// 在网格范围内随机生成坐标
food.x = Math.floor(Math.random() * gridWidth);
food.y = Math.floor(Math.random() * gridHeight);
// 检查食物是否生成在蛇身上,如果是则重新生成
const foodOnSnake = snake.some(segment => {
return segment.x === food.x && segment.y === food.y;
});
if (foodOnSnake) {
generateFood();
}
}
代码说明:
Math.random():生成0-1的随机数,配合Math.floor()得到整数坐标some()方法检查食物坐标是否与蛇身任何一段重合- 如果食物生成在蛇身上,递归调用自身重新生成,确保食物位置有效
七、步骤6:实现游戏核心逻辑
这是游戏最关键的部分,包括蛇的移动、碰撞检测和游戏状态更新:
// 更新游戏状态
function update() {
// 如果游戏结束则不更新
if (gameOver) return;
// 移动蛇:添加新头部,删除尾部
const head = { x: snake[0].x + dx, y: snake[0].y + dy };
snake.unshift(head); // 将新头部添加到数组开头
// 检查是否吃到食物
if (head.x === food.x && head.y === food.y) {
score += 10; // 加分
generateFood(); // 生成新食物(蛇身会变长,因为不删除尾部)
} else {
snake.pop(); // 没吃到食物,删除尾部(保持长度不变)
}
// 检查碰撞(边界和自身)
checkCollisions();
}
// 碰撞检测
function checkCollisions() {
const head = snake[0];
// 边界碰撞:检查头部是否超出画布范围
if (
head.x < 0 ||
head.x >= gridWidth ||
head.y < 0 ||
head.y >= gridHeight
) {
gameOver = true;
}
// 自身碰撞:检查头部是否与身体其他部分重合
for (let i = 1; i < snake.length; i++) {
if (head.x === snake[i].x && head.y === snake[i].y) {
gameOver = true;
}
}
}
代码说明:
- 蛇的移动原理:在头部前方添加一个新的头部,同时删除尾部(如果没吃到食物)
unshift():在数组开头添加新元素(新头部)pop():删除数组最后一个元素(尾部)- 碰撞检测分两种:撞到画布边界,或撞到自己的身体
八、步骤7:实现游戏主循环
游戏需要不断更新状态和重绘画面,这就是游戏循环:
// 游戏主循环
function gameLoop() {
// 清空画布
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制游戏元素
drawFood();
update(); // 先更新状态再绘制
drawSnake();
drawScore();
// 如果游戏结束,绘制结束画面
if (gameOver) {
drawGameOver();
return; // 游戏结束后不再循环
}
// 控制游戏速度(100毫秒刷新一次)
setTimeout(gameLoop, 100);
}
// 启动游戏
gameLoop();
代码说明:
- 游戏循环的执行流程:清空画布 → 更新游戏状态 → 绘制所有元素
setTimeout(gameLoop, 100):每100毫秒执行一次循环,控制游戏速度(数值越小速度越快)- 每次循环先清空画布,再重新绘制所有元素,形成动画效果
九、完整代码与运行效果
将以上所有代码整合到一起,保存后用浏览器打开,就能看到一个完整的贪吃蛇游戏:
- 使用方向键控制蛇的移动
- 吃到红色食物后蛇会变长,分数增加
- 撞到边界或自身时游戏结束
- 可按F5重新开始游戏
十、扩展功能建议(进阶练习)
如果你想进一步完善这个游戏,可以尝试添加这些功能:
- 难度递增:随着分数增加,减小
setTimeout的时间间隔,加快游戏速度 - 开始界面:添加游戏开始按钮,而不是直接启动
- 最高分记录:使用
localStorage保存玩家的最高分数 - 不同颜色的食物:特殊食物可获得更高分数或特殊效果(如短暂无敌)
- 移动音效:添加吃食物和碰撞的音效
- 触摸控制:为移动设备添加触摸滑动控制
通过这个项目,你已经掌握了HTML5 Canvas游戏开发的基本流程:创建画布、绘制元素、处理用户输入、实现游戏逻辑和循环。这些知识可以应用到其他Canvas游戏开发中,祝你编程愉快!

3万+

被折叠的 条评论
为什么被折叠?



