为什么onkeyup事件没有在javascript游戏中触发

时间:2012-06-27 12:00:02

标签: javascript javascript-events onkeyup onkeydown

我有一个2D游戏的开头 - 玩家可以使用箭头键在屏幕上围绕一个三角形。

问题在于,有时三角形将在一个方向上旋转或向前移动,直到再次按下相应的控制键并再次触发onkeyup事件。这通常发生在同时按下多个控制键时。

除非onkeyup事件因某种原因没有被解雇,否则我无法解决为什么它会陷入困境。非常感谢任何帮助,谢谢。

这里有一些代码,你可以找到fully working example on JSFiddle

...

function init(){
    var canvas = document.getElementById('canvas');
    if(canvas.getContext){
        setInterval(play, 50);
    }
}

function play(){
    printTriState();
    updateTriAcceleration();
    applyTriVelocity();
    updateTriRotation();
    draw();
}

document.onkeydown = function(event){

if(!event.keyCode){
    keycode = window.event.keyCode;
} else {
    keycode = event.keyCode;
}
console.log(event.keyCode);
switch(keycode){
    //left
    case 37:
    tri.rotation = -1;
    break;

    //up
    case 38:
    tri.throttle = true;
    break;

    //right
    case 39:
    tri.rotation = 1;
    break;

    //down
    case 40:
    tri.currentSpeed = 0;
    break;

    default:
    break;
}
};

document.onkeyup = function(event){

if(!event.keyCode){
    keycode = window.event.keyCode;
} else {
    keycode = event.keyCode;
}
console.log(event.keyCode);
switch(keycode){
    //left
    case 37:
    tri.rotation = 0;
    break;

    //up
    case 38:
    tri.throttle = false;
    break;

    //right
    case 39:
    tri.rotation = 0;
    break;

    //down
    case 40:

    break;

    default:
    break;
}
};

function updateTriRotation(){
    if(tri.rotation == 1){
        tri.orientation += tri.rotationSpeed;
    } else if (tri.rotation == -1){
        tri.orientation -= tri.rotationSpeed;
    } else {
        tri.orientation += 0;
    }
}

...

2 个答案:

答案 0 :(得分:11)

问题描述

keyup事件已被触发,但有时最终keydown事件会在keyup之后直接触发。奇怪的?是。

问题在于如何实现重复保持密钥的keydown事件:
重复触发最后一个保持密钥的keydown事件。由于优化的JavaScript代码执行的异步,单线程特性,有时会在触发相应的keydown事件后触发这样的重复keyup事件。

当我同时按住[UP]和[RIGHT]键in my JSFiddle example时,看看会发生什么。

Observed with Firefox 13.0.1 on Windows 7

rotation: 1 key down: 39
rotation: 1 key down: 38
rotation: 1 key down: 38
rotation: 1 key down: 38
rotation: 1 key up: 38
rotation: 0 key up: 39
rotation: 1 key down: 38

您可以在此示例控制台输出中看到三件事:

  1. 仅重复触发[RIGHT](38)的keydown事件。
  2. 当我发布密钥时,[右](38)keyup事件被触发。
  3. keydown执行完之后,
  4. 即使没有按下任何键,也会导致keyup状态“卡住”值为rotation的原因。


    解决方案

    为避免这种情况,您可以跟踪释放密钥时的微秒。当一个keydown事件发生只需几微秒时,我们就会默默地忽略它。

    我在您的代码中引入了一个辅助对象1See it working in this JSFiddle example.

    现在控制台输出如下:

    Key

    我的解决方案受到this blog article的启发,它更进一步,并解释了如何避免使用JavaScript关键事件进行游戏开发时的草率和“单向”问题。这绝对值得一读。

    忽略延迟rotation: 1 key down: 40 time: 1340805132040 rotation: 1 key down: 40 time: 1340805132071 rotation: 1 key down: 40 time: 1340805132109 rotation: 1 key up: 40 time: 1340805132138 rotation: 0 key up: 39 time: 1340805132153 key down: 40 release time: 1340804109335 keydown fired after keyup .. nothing to do. rotation: 0 key down: 39 time: 1340805132191 事件的解决方法基本上如下所示:

    keydown

    更新#1 Find an updated (fixed) version of your code on JSFiddle

答案 1 :(得分:2)

这个问题已经有将近8年的历史了,但是我遇到了类似的问题,并偶然发现了该页面,以防万一它对其他人有帮助:

我的代码中存在卡纸问题,该代码在键盘按下时使用Setadd键盘事件,在键盘按下时使用delete。我觉得使用event.key字段来区分按键是一种好习惯,而不是说keyCode来维护用户键盘上所写内容与我的代码所解释内容之间的语义映射。 (例如,我相信QWERTY键盘的W将报告与AZERTY键盘的Z相同的keyCode,但没有法语键盘可以进行验证。)

这很好用(我看不到其他人报告的乱序事件的问题),直到出现修饰符。我发现了两个问题:

  • ...按住Shift的位置。如果用户的击键顺序(保持键,保持键,R,释放键,释放键),则JS事件序列为wrt。 event.key就可以了(keydown-shift,keydown-R,keyup-shift,keyup-r)。换句话说,按下了R,但是释放了r,因此我的Set陷入了R的困境。我对此的解决方案是将Set转换为Map,其中键是event.keyCode,值是event.key,在按键事件的时间。这样,当我收到一个keyup事件时,我只需delete对应于keyCode的map键,键就不会卡住。我希望option / alt需要同样的处理。
  • ...保存Meta键的位置(在Mac上为Command;在Windows上为Control)。在这种情况下,继续执行的序列(hold-meta,hold-K,release-meta,release-K)将生成事件(keydown-meta,keydown-k,keyup-meta)。因此-即使我确定要进行event.preventDefault来防止系统窃取我的事件,也确实缺少K的keyup事件。我希望对此有一个更好的解决方案,但是当我看到Meta键事件时,我现在正在做的是整张地图。为了记录,我应该说我的代码实际上并没有使用Meta作为修饰符。我只想确保它的安全性,并且当用户快速键入内容时,我的密钥处理程序似乎很容易被卡住。

哦,还有一点模糊-我正在对地图进行核对。然后所有赌注都关闭了...