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

JavaScript贪吃蛇游戏Shift加速异常求助:长按Shift蛇停止移动

修复长按Shift键导致贪吃蛇暂停的问题

我一眼就看出问题所在啦——当你长按Shift键时,浏览器会重复触发keydown事件,你的代码每次都会清除当前定时器然后重新创建,频繁的重复操作会让定时器逻辑混乱,最后导致蛇停止移动。

下面是具体的修复方案,核心是加一个状态变量跟踪Shift键的按下状态,避免重复执行定时器的清除和重建:

修复步骤

  1. 添加isShiftPressed变量,用于标记Shift是否处于按下状态
  2. 在Shift的keydown事件中,仅当Shift未被按下时才执行加速操作
  3. 在Shift的keyup事件中恢复速度,并重置状态变量
  4. 游戏结束时也要重置Shift状态,避免后续状态混乱

修改后的完整代码

HTML部分

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="web_style.css">
    <title>Snake Game</title>
</head>
<body>
    <h1>Snake</h1>
    <canvas id="gc" width="400" height="400"></canvas>
    <script type="text/javascript" src="snake_script.js"></script>
</body>
</html>

CSS部分(web_style.css)

body { 
    background-color: lightblue; 
}
h1 { 
    color: white; 
    text-align: center; 
}
button { 
    margin-top: 20px; 
    line-height: 60px; 
    font-weight: bold; 
    padding: 0 40px; 
    background: salmon; 
    border: none; 
}
button:hover { 
    background: lightsalmon; 
}
canvas { 
    padding: 0; 
    margin: auto; 
    display: block; 
    width: 400px; 
    height: 400px; 
    position: absolute; 
    top: 0; 
    bottom: 0; 
    left: 0; 
    right: 0; 
}

JavaScript部分(snake_script.js)

var grid_size = 20;
var grid_num = 20;
var player_x = Math.floor(Math.random() * grid_num);
var player_y = Math.floor(Math.random() * grid_num);
var apple_x = Math.floor(Math.random() * grid_num);
var apple_y = Math.floor(Math.random() * grid_num);
var x_direction = 0;
var y_direction = 0;
var trail = [];
var tail = 5;
var normal_speed = 1000 / 10;
var speed = 1000 / 10;
var game_status = 0;
var my_game;
var isShiftPressed = false; // 新增状态变量跟踪Shift按键状态

// 创建游戏控制按钮
var button = document.createElement("button");
button.innerHTML = "Start/Resume Game";
var body = document.getElementsByTagName("body")[0];
body.appendChild(button);

button.addEventListener("click", function() {
    clearInterval(my_game);
    my_game = setInterval(game, speed);
});

window.onload = function() {
    canv = document.getElementById("gc");
    context = canv.getContext("2d");
    document.addEventListener("keydown", keyPush);
    document.addEventListener("keyup", keyRelease);
}

function game() {
    player_x += x_direction;
    player_y += y_direction;
    
    // 边界循环处理
    if (player_x < 0) player_x = grid_num - 1;
    if (player_x > grid_num - 1) player_x = 0;
    if (player_y < 0) player_y = grid_num - 1;
    if (player_y > grid_num - 1) player_y = 0;
    
    // 绘制游戏背景
    context.fillStyle = "black";
    context.fillRect(0, 0, canv.width, canv.height);
    
    // 绘制蛇身
    context.fillStyle = "lime";
    for(var i = 0; i < trail.length; i++) {
        context.fillRect(trail[i].x * grid_size, trail[i].y * grid_size, grid_size - 2, grid_size - 2);
        // 游戏开始后才触发碰撞结束逻辑
        if(trail[i].x == player_x && trail[i].y == player_y && game_status !== 0) {
            endGame();
            return;
        }
    }
    
    trail.push({x:player_x, y:player_y});
    while(trail.length > tail) trail.shift();
    
    // 吃苹果逻辑
    if (apple_x == player_x && apple_y == player_y) {
        tail++;
        apple_x = Math.floor(Math.random() * grid_num);
        apple_y = Math.floor(Math.random() * grid_num);
    }
    
    // 绘制苹果
    context.fillStyle = "red";
    context.fillRect(apple_x * grid_size, apple_y * grid_size, grid_size - 2, grid_size - 2);
}

function keyPush(keyEvent) {
    switch(keyEvent.keyCode) {
        // 左方向键/A键控制
        case 37:
        case 65:
            if (x_direction == 1) break;
            game_status++;
            x_direction = -1;
            y_direction = 0;
            break;
        // 上方向键/W键控制
        case 38:
        case 87:
            if (y_direction == 1) break;
            game_status++;
            x_direction = 0;
            y_direction = -1;
            break;
        // 右方向键/D键控制
        case 39:
        case 68:
            if (x_direction == -1) break;
            game_status++;
            x_direction = 1;
            y_direction = 0;
            break;
        // 下方向键/S键控制
        case 40:
        case 83:
            if (y_direction == -1) break;
            game_status++;
            x_direction = 0;
            y_direction = 1;
            break;
        // Shift键加速:仅当未按下时执行一次
        case 16:
            if (!isShiftPressed) {
                clearInterval(my_game);
                speed = normal_speed * 0.5;
                my_game = setInterval(game, speed);
                isShiftPressed = true;
            }
            break;
    }
}

function keyRelease(keyEvent) {
    switch(keyEvent.keyCode) {
        case 16:
            isShiftPressed = false;
            clearInterval(my_game);
            speed = normal_speed;
            my_game = setInterval(game, speed);
            break;
    }
}

function endGame() {
    clearInterval(my_game);
    context.fillStyle = "yellow";
    context.font="20px Georgia";
    context.fillText("Game Over!", 10, 50);
    
    // 重置所有游戏状态(包括Shift按键状态)
    x_direction = 0;
    y_direction = 0;
    trail = [];
    tail = 5;
    normal_speed = 1000 / 10;
    speed = 1000 / 10;
    game_status = 0;
    isShiftPressed = false;
}

另外我还顺手优化了几个小细节:

  • 合并了方向键和WASD的重复代码,让逻辑更简洁
  • 调整了按钮文字为更清晰的"Start/Resume Game"
  • 优化了边界判断的写法,去掉冗余的大括号
  • 明确了游戏开始状态(game_status !==0)才触发碰撞结束,避免初始状态误判

这样修改后,长按Shift键就只会触发一次加速操作,松开时恢复正常速度,再也不会出现游戏暂停的问题啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:49:02