在 Javascript 游戏中同时处理 KeyEvents

Handling simultaneous KeyEvents in a Javascript Game

我正在 Javascript 开发一款角色扮演游戏,正在为主要玩家设置控件,但我在处理 keyEvent 时遇到了一些问题。此代码控制玩家移动和动画。哪里出了问题,我相信,如果游戏在已经有另一个按键的情况下注册了一个按键,然后第一个按键上升,精灵暂停(isMoving 属性 基本上停止移动和动画,当它是假)。

// For example:
// I hold W. Sprite begins moving up.
// While holding W, I begin to hold D. Sprite begins moving right.
// I release W. Sprite should keep going right, but pauses momentarily when the
// keyup is registered

var move = function(e) {

    xavier.isMoving = true;

    switch(e.keyCode)
    { 
        case 87: xavier.direction = 'up';    break; //W
        case 65: xavier.direction = 'left';  break; //A
        case 68: xavier.direction = 'right'; break; //D
        case 83: xavier.direction = 'down';  break; //S 
    };
};
var wait = function(e) {
    xavier.isMoving = false;
};    
window.addEventListener("keydown", move, false);
window.addEventListener("keyup", wait, false);

是否有更好的方法来设置控件以便我的游戏可以同时处理多个按键?

此外,按下 mac 命令键时精灵会移动。不知道为什么。

当然有

您需要一个数组和 2 个事件侦听器,一个用于 keydown,一个用于 keyup

每当有人按下按钮时,将id与按钮的keyCode相同的元素设置为true。如果您的数组名为 keys,那么它看起来像这样:

keys[e.keyCode] = true;

然后,每当用户释放一个按钮时,将与该按钮的keyCode具有相同id的元素设置为false:

keys[e.keyCode] = false;

稍后,您可以检查与您希望在按下后发生特定事件的按钮具有相同 id 的元素是否为真。

var move = function(e) {

    xavier.isMoving = true;

    switch(e.keyCode)
    { 
        case 87: xavier.direction.up = true;    break; //W
        case 65: xavier.direction.left = true;  break; //A
        case 68: xavier.direction.right = true; break; //D
        case 83: xavier.direction.down = true;  break; //S 
    };
};
var wait = function(e) {
    switch(e.keyCode)
    { 
        case 87: xavier.direction.up = true;    break; //W
        case 65: xavier.direction.left = true;  break; //A
        case 68: xavier.direction.right = true; break; //D
        case 83: xavier.direction.down = true;  break; //S 
    };
    xavier.isMoving = xavier.direction.up ||
      xavier.direction.left || xavier.direction.right ||
      xavier.direction.down;
};    
window.addEventListener("keydown", move, false);
window.addEventListener("keyup", wait, false);

这是一个解决方案,可以跟踪按下的键(方向)并在释放第二个键时返回到另一个按下的键(方向)。而且这两个功能非常相似,所以合二为一:

var xavier = {};
xavier.isMoving = false;
xavier.stack = [];
xavier.direction = '';
xavier.directions = {
    87: 'up',    //W
    65: 'left',  //A
    68: 'right', //D
    83: 'down',  //S 
};

var move = function(e) {
    // get direction from mapping: key -> direction
    var direction = xavier.directions[e.keyCode];
    if (!direction) return; // not a move-key
    var i = xavier.stack.indexOf(direction);
    if (e.type === 'keydown' && i === -1) {
        // If this key was not yet down, but is pressed, 
        // then add the direction to the list
        xavier.stack.push(direction);
    } else if (e.type === 'keyup' && i !== -1) {
        // If this key was down, but is released, 
        // then remove the direction from the list
        xavier.stack.splice(i, 1);
    }
    xavier.direction = xavier.stack.length ? xavier.stack[xavier.stack.length-1]
                                           : '';
    xavier.isMoving = xavier.direction !== '';
    // Test for this snippet only:
    document.body.textContent = xavier.direction;
};

window.addEventListener("keydown", move, false);
window.addEventListener("keyup", move, false);
Click here (to focus) and press/release ASQW keys

请注意,理论上这支持同时按下任意数量的键,但实际上键盘通常限制为可以按下的数量。一旦超过此限制,将不会生成任何按键事件。

您需要取消关键事件与动画或主游戏循环的关联。 keydown, keyup 事件是 IO 事件,不应该处理游戏逻辑,它们可以以任何速率进入,你可能在帧之间有很多,并且在每个帧上应用逻辑只是浪费处理,如果你有更多的逻辑,更糟糕的是玩家可能会错过逻辑事件,因为关键事件在主循环没有处理新位置的情况下已经进行。鼠标和触摸事件也是如此,你应该只记录事件并在游戏内处理。

// define keys and an array to keep key states
// global key log;
var keyState = [];
const KEY_UP = 38;
const KEY_DOWN = 40;
const KEY_LEFT = 37;
const KEY_RIGHT = 39;


// create a logging function
const keyEventLogger =  function (e) {  keyState[e.keyCode] = e.type == 'keydown';}
window.addEventListener("keydown", keyEventLogger);
window.addEventListener("keyup", keyEventLogger);


// define something to move
var player = {y : 100, x: 100};
const MOVE_SPEED = 2;

// in the main loop;
function update(timer) {
    if (keyState[KEY_UP]) {
        player.y -= MOVE_SPEED;
    } 
    if (keyState[KEY_DOWN]) {
        player.y += MOVE_SPEED;
    }
    if (keyState[KEY_LEFT]) {
        player.x -= MOVE_SPEED;
    }
    if (keyState[KEY_RIGHT]) {
        player.x += MOVE_SPEED;
    }
    requestAnimationFrame(update);

}
requestAnimationFrame(update);