贪吃蛇游戏实现

AI 智能总结

简介

贪吃蛇是经典街机游戏,玩家控制蛇的方向吃食物,蛇身不断增长,碰到墙壁或自身则游戏结束。游戏厅版本使用 Canvas 渲染,支持方向键控制和最高分记录。

数据结构

蛇身表示

蛇身使用数组存储,每个元素是 {x, y} 坐标对。数组头部为蛇头,尾部为蛇尾:

1var snake = [
2  {x: 10, y: 10},  // 蛇头
3  {x: 9, y: 10},   // 蛇身
4  {x: 8, y: 10}    // 蛇尾
5];
6var direction = {x: 1, y: 0}; // 初始向右移动

网格系统

游戏区域划分为 20x20 的网格:

1var GRID_SIZE = 20;
2var CELL_SIZE = 20; // 每格像素大小
3var canvasW = GRID_SIZE * CELL_SIZE;
4var canvasH = GRID_SIZE * CELL_SIZE;

核心逻辑

移动与增长

每次移动时,在蛇头前方插入新头部,如果没吃到食物则移除尾部:

 1function move() {
 2  // 计算新蛇头位置
 3  var head = snake[0];
 4  var newHead = {
 5    x: head.x + direction.x,
 6    y: head.y + direction.y
 7  };
 8
 9  // 穿墙:从另一边出现
10  if (newHead.x < 0) newHead.x = GRID_SIZE - 1;
11  if (newHead.x >= GRID_SIZE) newHead.x = 0;
12  if (newHead.y < 0) newHead.y = GRID_SIZE - 1;
13  if (newHead.y >= GRID_SIZE) newHead.y = 0;
14
15  snake.unshift(newHead);
16
17  // 检查是否吃到食物
18  if (newHead.x === food.x && newHead.y === food.y) {
19    score += 10;
20    spawnFood();
21    // 不删除尾部 = 蛇身增长
22  } else {
23    snake.pop(); // 删除尾部 = 保持长度
24  }
25}

碰撞检测

蛇头碰到自身任意部位则游戏结束:

1function checkCollision() {
2  var head = snake[0];
3  for (var i = 1; i < snake.length; i++) {
4    if (head.x === snake[i].x && head.y === snake[i].y) {
5      return true;
6    }
7  }
8  return false;
9}

食物生成

在空白位置随机生成食物,避免生成在蛇身上:

 1function spawnFood() {
 2  var emptyCells = [];
 3  for (var x = 0; x < GRID_SIZE; x++)
 4    for (var y = 0; y < GRID_SIZE; y++) {
 5      if (snake.some(function(seg) { return seg.x === x && seg.y === y; })) continue;
 6      emptyCells.push({x: x, y: y});
 7    }
 8  if (emptyCells.length === 0) {
 9    // 胜利!蛇已占满整个网格
10    winGame();
11    return;
12  }
13  var idx = Math.floor(Math.random() * emptyCells.length);
14  food = emptyCells[idx];
15}

方向控制

使用方向键改变蛇的移动方向,禁止反向移动(如向右时不能直接向左):

 1document.addEventListener('keydown', function(e) {
 2  if (!isActive()) return;
 3  switch(e.key) {
 4    case 'ArrowUp':    if (direction.y === 0) direction = {x: 0, y: -1}; break;
 5    case 'ArrowDown':  if (direction.y === 0) direction = {x: 0, y: 1};  break;
 6    case 'ArrowLeft':  if (direction.x === 0) direction = {x: -1, y: 0}; break;
 7    case 'ArrowRight': if (direction.x === 0) direction = {x: 1, y: 0};  break;
 8  }
 9  e.preventDefault();
10});

Canvas 渲染

绘制蛇身

蛇头使用较深的绿色,蛇身使用渐变色:

 1function drawSnake() {
 2  snake.forEach(function(seg, i) {
 3    var x = seg.x * CELL_SIZE, y = seg.y * CELL_SIZE;
 4    if (i === 0) {
 5      // 蛇头:深绿色
 6      ctx.fillStyle = '#166534';
 7      ctx.fillRect(x + 1, y + 1, CELL_SIZE - 2, CELL_SIZE - 2);
 8      // 眼睛
 9      ctx.fillStyle = '#fff';
10      ctx.fillRect(x + 5, y + 5, 4, 4);
11      ctx.fillRect(x + 11, y + 5, 4, 4);
12    } else {
13      // 蛇身:绿色渐变
14      ctx.fillStyle = '#22c55e';
15      ctx.fillRect(x + 1, y + 1, CELL_SIZE - 2, CELL_SIZE - 2);
16    }
17  });
18}

绘制食物

食物使用红色圆形,带发光效果:

 1function drawFood() {
 2  var cx = food.x * CELL_SIZE + CELL_SIZE / 2;
 3  var cy = food.y * CELL_SIZE + CELL_SIZE / 2;
 4  // 光晕
 5  ctx.fillStyle = 'rgba(239, 68, 68, 0.3)';
 6  ctx.beginPath();
 7  ctx.arc(cx, cy, CELL_SIZE / 2 + 2, 0, Math.PI * 2);
 8  ctx.fill();
 9  // 食物本体
10  ctx.fillStyle = '#ef4444';
11  ctx.beginPath();
12  ctx.arc(cx, cy, CELL_SIZE / 2 - 2, 0, Math.PI * 2);
13  ctx.fill();
14}

游戏循环

使用 requestAnimationFrame 实现游戏循环,通过帧计数器控制蛇的移动速度:

 1var frameCount = 0;
 2var SPEED = 8; // 每 8 帧移动一次
 3
 4function gameLoop() {
 5  if (!playing) return;
 6  frameCount++;
 7  if (frameCount >= SPEED) {
 8    frameCount = 0;
 9    move();
10    if (checkCollision()) {
11      gameOver();
12      return;
13    }
14  }
15  ctx.clearRect(0, 0, canvasW, canvasH);
16  drawGrid();
17  drawFood();
18  drawSnake();
19  animFrameId = requestAnimationFrame(gameLoop);
20}

速度递增

随着分数提高,蛇的移动速度逐渐加快:

1function getSpeed() {
2  if (score >= 200) return 3;
3  if (score >= 150) return 4;
4  if (score >= 100) return 5;
5  if (score >= 50) return 6;
6  return 8;
7}

最高分持久化

1function saveBest() {
2  var best = localStorage.getItem('arcade-snake-best');
3  if (!best || score > parseInt(best)) {
4    localStorage.setItem('arcade-snake-best', score);
5  }
6}

总结

贪吃蛇的核心在于数组模拟蛇身移动(头部插入 + 尾部删除)和碰撞检测。Canvas 渲染提供了流畅的动画效果,帧计数器控制移动速度实现了速度递增的难度曲线。

留言评论

期待你的想法

评论加载中