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

JavaScript键盘重复延迟问题:WASD控制游戏移动不流畅求助

WASD角色移动卡顿问题修复

我制作了一款通过WASD按键控制角色移动的游戏,但实际效果不符合预期:每次按下按键时,角色先移动一小步,之后会无延迟重复移动,导致画面卡顿不流畅。

原代码如下:

var Player = document.getElementById("Player");
const SpeedX = 35;
const SpeedY = 20;
var x = parseInt(getComputedStyle(Player).marginLeft);
var y = parseInt(getComputedStyle(Player).marginTop); 

var keysPressed = {};

document.addEventListener("keydown", (e) => {
        keysPressed[e.key] = true;

        if(e.key == 'w') {
        if(y > 30 && y < 950) {
            y -= SpeedY;
            Player.style.marginTop = y + "px";
            console.log(y);
        } else if(y == 950) Player.style.marginTop = (y -= SpeedY) + "px";
    }
        if(e.key == 'a') {
        if(x > -5 && x < 1780) {
            x -= SpeedX;
            Player.style.marginLeft = x + "px";
            console.log(x);
        } else if(x == 1780) Player.style.marginLeft = (x -= SpeedX) + "px";
    }
        if(e.key == 's') {
        if(y > 30 && y < 950) {
            y += SpeedY;
            Player.style.marginTop = y + "px";
            console.log(y);
        } else if(y == 30) Player.style.marginTop = (y += SpeedY) + "px";
    }
        if(e.key == 'd') {
        if(x > -5 && x < 1780) {
            x += SpeedX;
            Player.style.marginLeft = x + "px";
            console.log(x);
        } else if(x == -5) Player.style.marginLeft = (x += SpeedX) + "px";
    }
        if(e.key == 'w' && keysPressed['a']) {
            if(x > -5 && x < 1780 && y > 30 && y < 950) {
            y -= SpeedY;
            x -= SpeedX;
            Player.style.marginLeft = x + "px";
            Player.style.marginTop = y + "px";
        }}
        if(e.key == 'w' && keysPressed['d']) {
            if(x > -5 && x < 1780 && y > 30 && y < 950) {
            y -= SpeedY;
            x += SpeedX;
            Player.style.marginLeft = x + "px";
            Player.style.marginTop = y + "px";
        }}
        if(e.key == 's' && keysPressed['a']) {
            if(x > -5 && x < 1780 && y > 30 && y < 950) {
            y += SpeedY;
            x -= SpeedX;
            Player.style.marginLeft = x + "px";
            Player.style.marginTop = y + "px";
        }}
        if(e.key == 's' && keysPressed['d']) {
            if(x > -5 && x < 1780 && y > 30 && y < 950) {
            y += SpeedY;
            x += SpeedX;
            Player.style.marginLeft = x + "px";
            Player.style.marginTop = y + "px";
        }}
        if(e.key == 'a' && keysPressed['w']) {
            if(x > -5 && x < 1780 && y > 30 && y < 950) {
            y -= SpeedY;
            x -= SpeedX;
            Player.style.marginLeft = x + "px";
            Player.style.marginTop = y + "px";
        }}
        if(e.key == 'd' && keysPressed['w']) {
            if(x > -5 && x < 1780 && y > 30 && y < 950) {
            y -= SpeedY;
            x += SpeedX;
            Player.style.marginLeft = x + "px";
            Player.style.marginTop = y + "px";
        }}
        if(e.key == 'a' && keysPressed['s']) {
            if(x > -5 && x < 1780 && y > 30 && y < 950) {
            y += SpeedY;
            x -= SpeedX;
            Player.style.marginLeft = x + "px";
            Player.style.marginTop = y + "px";
        }}
        if(e.key == 'd' && keysPressed['s']) {
            if(x > -5 && x < 1780 && y > 30 && y < 950) {
            y += SpeedY;
            x += SpeedX;
            Player.style.marginLeft = x + "px";
            Player.style.marginTop = y + "px";
        }}
    })

document.addEventListener('keyup', (event) => {
    delete keysPressed[event.key];
});

问题原因

  • 依赖keydown事件触发移动:系统默认键盘按键重复有初始延迟,第一次按下后会先触发一次,间隔一小段时间才会连续触发,导致移动卡顿。
  • 多按键判断冗余:同一组合按键的判断逻辑重复,可能导致单次按键触发多次移动计算,干扰流畅度。

修复方案

改用requestAnimationFrame实现游戏循环,实时检测按键状态,和屏幕刷新率同步更新角色位置,彻底解决延迟问题,同时简化多按键判断逻辑:

修改后的代码:

var Player = document.getElementById("Player");
const SpeedX = 35;
const SpeedY = 20;
var x = parseInt(getComputedStyle(Player).marginLeft);
var y = parseInt(getComputedStyle(Player).marginTop); 

var keysPressed = {};

// 记录按键状态
document.addEventListener("keydown", (e) => {
    keysPressed[e.key.toLowerCase()] = true;
});

document.addEventListener('keyup', (event) => {
    delete keysPressed[event.key.toLowerCase()];
});

// 游戏循环
function gameLoop() {
    let dx = 0;
    let dy = 0;

    // 检测按键状态,计算位移
    if (keysPressed['w']) dy -= SpeedY;
    if (keysPressed['s']) dy += SpeedY;
    if (keysPressed['a']) dx -= SpeedX;
    if (keysPressed['d']) dx += SpeedX;

    // 边界判断
    const newX = x + dx;
    const newY = y + dy;
    if (newX > -5 && newX < 1780) x = newX;
    if (newY > 30 && newY < 950) y = newY;

    // 统一更新位置
    Player.style.marginLeft = x + "px";
    Player.style.marginTop = y + "px";

    // 持续循环
    requestAnimationFrame(gameLoop);
}

// 启动循环
gameLoop();

关键优化点

  1. 分离按键记录与移动逻辑:keydown和keyup只负责维护按键状态,不处理移动计算。
  2. 游戏循环同步屏幕刷新:requestAnimationFrame会在浏览器每次重绘前执行,保证移动动画和屏幕刷新率一致,消除卡顿。
  3. 简化多按键处理:直接叠加多个按键的位移,自然支持斜向移动,无需重复判断组合按键。
  4. 统一边界判断:先计算目标位置,再判断是否在边界内,避免冗余的条件分支。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 00:15:24