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

Rob*_*yes 3 javascript javascript-events onkeydown onkeyup

我有一个2D游戏的开始 - 玩家可以使用箭头键在屏幕上驱动三角形.

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

我无法理解为什么它会被困在第一位,除非onkeyup事件因某些原因没有被解雇.非常感谢任何帮助,谢谢.

这里有一些代码,你可以在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;
    }
}

...
Run Code Online (Sandbox Code Playgroud)

Kai*_*aii 11

问题描述

这些keyup事件解雇了,但有时最后的keydown事件会在事件发生之后直接触发keyup.奇怪的?是.

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

看看我在JSFiddle示例中同时按住并释放[UP]和[RIGHT]键时会发生什么.

在Windows 7上使用Firefox 13.0.1观察:

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
Run Code Online (Sandbox Code Playgroud)

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

  1. keydown重复触发[RIGHT](38)的事件.
  2. 当我释放钥匙时keyup,[RIGHT](38)的事件触发.
  3. 执行完后,keydown触发最后一个计划的事件.keyup

这就是为什么即使没有按下按键,你的rotation状态也会"卡住"价值的原因1.


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

Key在你的代码中引入了一个辅助对象.看看它在这个JSFiddle示例中工作.

现在控制台输出如下:

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
Run Code Online (Sandbox Code Playgroud)

我的解决方案受到了这篇博客文章的启发,该文章更进一步,并解释了如何避免在使用JavaScript关键事件进行游戏开发时出现的草率和"单向"问题.这绝对值得一读.

忽略延迟keydown事件的解决方法基本上如下所示:

var Key = {
    _pressed: {},
    _release_time: {},

    MAX_KEY_DELAY: 100,

    onKeydown: function(event) {

        // avoid firing of the keydown event when a keyup occured
        // just +/- 100ms before or after a the keydown event.
        // due to the asynchronous nature of JS it may happen that the
        // timestamp of keydown is before the timestamp of keyup, but 
        // the actual execution of the keydown event happens *after* 
        // the keyup.   Weird? Yes. Confusing!

        var time = new Date().getTime();
        if ( this._release_time[event.keyCode] &&
             time < this._release_time[event.keyCode] + this.MAX_KEY_DELAY ) {
            console.log('keydown fired after keyup .. nothing to do.');
            return false;
        }

        this._pressed[event.keyCode] = true;

        // LOGIC FOR ACTUAL KEY CODE HANDLING GOES HERE
    },

    onKeyup: function(event) {
        delete this._pressed[event.keyCode];
        this._release_time[event.keyCode] = new Date().getTime();

        // LOGIC FOR ACTUAL KEY CODE HANDLING GOES HERE
    }
};

document.onkeydown = function(event) { return Key.onKeydown(event); };
document.onkeyup = function(event) { return Key.onKeyup(event); };
Run Code Online (Sandbox Code Playgroud)

更新#1 在JSFiddle上查找代码的更新(固定)版本.

  • 这个答案是惊人的! (2认同)

小智 7

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

\n\n

我的代码中遇到了干扰问题,该代码在按下键盘时使用Set键盘事件,然后在按下键盘时使用键盘事件。我觉得使用该字段来区分按键(而不是使用 )来维护用户键盘上写入的内容和我的代码解释的内容之间的语义映射是一种很好的做法。(例如,我相信 QWERTY 键盘的 W 会报告相同的结果adddeleteevent.keykeyCodekeyCode与 AZERTY 键盘的 Z 相同,但没有法语键盘可供验证。)

\n\n

这工作得很好(我没有看到其他人报告的无序事件的问题),直到修改器出现为止。我发现了两个问题:

\n\n
    \n
  • ...Shift 举行的地方。如果用户的击键顺序为(hold-shift、hold-R、release-shift、release-R),则 JS 事件的顺序为 wrt。将会event.key进行(keydown-shift、keydown-R、keyup-shift、keyup-r)。换句话说, anR被按下,但 anr被释放,所以 mySetR卡住了。我的解决方案是将 变成Seta Map,其中键是event.keyCode,值是event.key在 keydown 事件时报告的 。这样,当我收到 keyup 事件时,我只需delete对应于的地图键keyCode键不会被卡住。我预计 option/alt 需要相同的处理。
  • \n
  • ...Meta 键所在的位置(Mac 上的 Command;我猜 Windows 上的 Control?)。在这种情况下,执行的序列(hold-meta、hold-K、release-meta、release-K)将生成事件(keydown-meta、keydown-k、keyup-meta)。所以 \xe2\x80\x94\xc2\xa0 K 的 keyup 事件确实丢失了,即使我确信这样做event.preventDefault以防止系统窃取我的事件。我希望我有一个更好的解决方案,但我现在正在做的是当我看到元关键事件时对整个地图进行核攻击。我应该郑重声明一下,我的代码实际上并没有使用 Meta 作为修饰符;我只是想确保它是安全的,当用户快速打字时,我的按键处理程序似乎很容易被卡住。
  • \n
\n\n

哦,还有,在模糊 \xe2\x80\x94\xc2\xa0 上,我正在核对地图。那么所有的赌注都取消了......

\n