MMyBlog
← 返回博客
6 分钟·2,332 ·37 次阅读

贪吃蛇小游戏:从零实现一个带排行榜的经典游戏

贪吃蛇小游戏:从零实现一个带排行榜的经典游戏

前言

贪吃蛇是我接触编程时最早想做的游戏之一。规则简单——控制蛇吃食物,不能碰墙也不能碰自己。这次把它做成了一个带排行榜的完整版本,放在博客的 Playground 里。

? 点这里玩贪吃蛇


技术实现

Canvas 绘制

用 HTML5 Canvas 绘制游戏画面,20x20 的网格,蛇和食物都在网格上移动:

const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const GRID = 20;

// 绘制蛇身(渐变透明度)
snake.forEach((seg, i) => {
  const alpha = 1 - (i / snake.length) * 0.5;
  ctx.fillStyle = i === 0 ? '#22d3ee' : `rgba(34, 211, 238, ${alpha})`;
  ctx.fillRect(seg.x * GRID + 1, seg.y * GRID + 1, GRID - 2, GRID - 2);
});

蛇头用青色,身体逐渐变淡,形成拖尾效果。

游戏逻辑

核心逻辑很简单:每帧蛇头向前移动一格,如果吃到食物就增长,否则去掉尾巴。

function update() {
  const head = { x: snake[0].x + dir.x, y: snake[0].y + dir.y };

  // 碰壁或碰自己 → 游戏结束
  if (head.x < 0 || head.x >= COLS || head.y < 0 || head.y >= ROWS ||
      snake.some(s => s.x === head.x && s.y === head.y)) {
    gameOver();
    return;
  }

  snake.unshift(head);

  if (head.x === food.x && head.y === food.y) {
    score += 10;
    placeFood();
  } else {
    snake.pop();
  }
}

键盘控制

支持方向键和 WASD,加了一个防止反向移动的判断(不能直接掉头):

if (newDir.x !== -dir.x || newDir.y !== -dir.y) {
  dir = newDir;
}

还支持空格暂停/继续。


排行榜功能

排行榜用数据库实现,游戏结束时弹出输入框,提交分数后实时更新排行榜。

数据库设计

model SnakeScore {
  id        String   @id @default(cuid())
  name      String   @db.VarChar(50)
  score     Int
  createdAt DateTime @default(now())

  @@index([score(sort: Desc)])
}

直接在数据库层按分数降序建索引,查询 Top 10 走索引,不需要内存排序。

API 设计

// GET: 排行榜 Top 10
const scores = await db.snakeScore.findMany({
  take: 10,
  orderBy: { score: "desc" },
});

// POST: 提交分数
await db.snakeScore.create({
  data: { name, score },
});

前端在游戏结束时调用 POST 接口,提交后自动刷新排行榜。


界面设计

深色主题,和博客管理后台风格统一。左侧是游戏区域,右侧是排行榜。

排行榜前三名用金银铜色徽章标识,增加成就感。移动端自动变成上下布局。


一些细节

蛇身渐变:蛇头最亮,身体逐渐变淡,形成拖尾效果。这样即使蛇很长,也能一眼看出蛇头在哪。

蛇头眼睛:在蛇头上画了两个小圆点作为眼睛,方向会跟着移动方向变化。

食物动画:食物用圆形绘制,比方块更好看。

暂停功能:按空格键暂停/继续,暂停时显示"已暂停"状态。


试试看

? 点这里玩贪吃蛇

玩完记得输入名字,登上排行榜!


更新于 2026年8月26日

文章链接: