You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript贪吃蛇游戏:如何避免重复按键导致蛇加速

解决贪吃蛇狂按按键导致异常加速的问题

问题根源

你当前的代码每次按下W/A/S/D时,都会调用对应方向的xxxLoop()函数,而每个xxxLoop()内部会递归创建新的setTimeout定时器。狂按按键会同时启动多个独立的定时器,这些定时器都会触发蛇的移动逻辑,最终导致移动速度叠加,出现异常加速的情况。

最优解决方案:单主循环+方向状态控制

改用单个游戏主循环控制蛇的移动,按键仅修改当前方向状态,不直接启动新的定时器。这种方式从根源上避免了多个定时器叠加的问题,同时还能限制蛇不能反向移动(防止直接撞自己)。

修改后的完整代码

HTML(无需修改)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="./style.css">
  <script src="./game.js" defer></script>
  <title>Snake Game</title>
</head>
<body>
  <div id="game-board">
    <div class="snake" id="snake"></div>
    <div class="apple" id="apple"></div>
  </div>
</body>
</html>

CSS(无需修改)

body{
  height:100vh;
  width:100vw;
  display: flex;
  justify-content: center;
  align-items: center;
  margin:0;
  background-color: darkgreen;
}
#game-board{
  background-color: green;
  width:500px;
  height:500px;
  display: grid;
  grid-template-rows: repeat(20, 1fr);
  grid-template-columns: repeat(20, 1fr);
}

.snake{
  background-color: rgb(65, 65, 255);
  border: 2px solid black;
  border-radius: 5px;
  z-index: 99999;
}

.apple{
  background-color: rgb(248, 24, 24);
  border: 2px solid black;
  border-radius: 5px;
}

JavaScript(重构核心逻辑)

let apple = document.getElementById('apple');
let snake = document.getElementById('snake');
let snakeColumnStart = 11;
let snakeRowStart = 11;
let currentDirection = null; // 记录当前移动方向:'up'/'right'/'left'/'down'
let gameOver = false;

// 生成苹果随机位置
const getApplePos = (min, max) => {
  return Math.floor(Math.random() * (max - min + 1) + min);
}

// 设置苹果位置
const setApplePos = () => {
  const gridColumnStart = getApplePos(1, 20);
  const gridColumnEnd = gridColumnStart + 1;
  const gridRowStart = getApplePos(1, 20);
  const gridRowEnd = gridRowStart + 1;
  apple.style.gridArea = `${gridRowStart} / ${gridColumnStart} / ${gridRowEnd} / ${gridColumnEnd}`;
}

// 设置蛇的位置
const setSnakePos = () => {
  let snakeColumnEnd = snakeColumnStart + 1;  
  let snakeRowEnd = snakeRowStart + 1;
  snake.style.gridArea = `${snakeRowStart} / ${snakeColumnStart} / ${snakeRowEnd} / ${snakeColumnEnd}`;
}

// 游戏主循环:统一控制移动逻辑
const gameLoop = () => {
  setTimeout(() => {
    if (gameOver) return;

    // 根据当前方向更新蛇的位置
    switch(currentDirection) {
      case 'up':
        snakeRowStart -= 1;
        break;
      case 'right':
        snakeColumnStart += 1;
        break;
      case 'left':
        snakeColumnStart -= 1;
        break;
      case 'down':
        snakeRowStart += 1;
        break;
    }

    // 边界检测,触碰到边界则游戏结束
    if (snakeRowStart < 1 || snakeRowStart > 20 || snakeColumnStart < 1 || snakeColumnStart > 20) {
      console.log("game over");
      gameOver = true;
      return;
    }

    setSnakePos();
    gameLoop(); // 递归调用,维持循环
  }, 1000); // 移动间隔,可调整速度
}

// 键盘事件监听:仅修改方向状态,禁止反向切换
window.addEventListener('keydown', (e) => {
  switch(e.key) {
    case 'w':
      // 当前不是向下时,才能切换为向上
      if (currentDirection !== 'down') currentDirection = 'up';
      break;
    case 'd':
      // 当前不是向左时,才能切换为向右
      if (currentDirection !== 'left') currentDirection = 'right';
      break;
    case 'a':
      // 当前不是向右时,才能切换为向左
      if (currentDirection !== 'right') currentDirection = 'left';
      break;
    case 's':
      // 当前不是向上时,才能切换为向下
      if (currentDirection !== 'up') currentDirection = 'down';
      break;
  }
});

// 初始化游戏
setSnakePos();
setApplePos();
gameLoop(); // 启动主循环

代码说明

  1. 单主循环:用gameLoop()统一控制蛇的移动,只有一个定时器在运行,彻底避免多定时器叠加的问题。
  2. 方向状态管理:用currentDirection记录当前方向,按键仅修改这个状态,不触发新的定时器。
  3. 禁止反向移动:添加判断逻辑,防止蛇直接反向移动(比如向上时不能直接按向下),符合贪吃蛇的常规规则。

内容的提问来源于stack exchange,提问作者user19559860

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 11:16:02