-->

为什么在JavaScript游戏不触发事件的onkeyup(why are onkeyup even

2019-09-18 15:03发布

我有一个2D的JavaScript游戏的开端 - 此刻,玩家可以使用箭头键驱动器在屏幕上的三角形。

问题是,有时三角形将陷入一个方向旋转或向前移动,直到相应的控制键被再次按下和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;
    }
}

...

Answer 1:

Problem description

The keyup events are fired, but sometimes a final keydown event fires directly after the keyup. Weird? Yes.

The problem is the way how the repetition of the keydown event for a held key is implemented:
the keydown event for the last held key is fired repeatedly. Due to the asynchronous, single-threaded nature of optimized JavaScript code execution, it can happen sometimes that such a repeated keydown event is fired after the corresponding keyup event was fired.

See what happens when i hold and release the keys [UP] and [RIGHT] simultaneously 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

You can see three things in this example console output:

  1. only the keydown event for [RIGHT] (38) is fired repeatedly.
  2. The keyup event for [RIGHT] (38) is fired when i release the key.
  3. A last scheduled keydown event is fired, after the keyup was executed.

Thats the reason why your rotation state is "stuck" with value 1 even if no key is pressed.


Solution

To avoid this, you can keep track of the microseconds when a key is released. When a keydown event occurs just a few microseconds ahead, then we silently ignore it.

I introduced a helper object Key into your code. See it working in this JSFiddle example.

Now the console output looks like:

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

My solution is inspired by this blog article, which goes a bit further and explains how to avoid the sloppyness and "one-direction-only" issues you have when using JavaScript key events for game development. It's definitely worth reading.

The workaround to ignore the delayed keydown event basically looks like this:

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); };

Update #1 Find an updated (fixed) version of your code on JSFiddle.



文章来源: why are onkeyup events not firing in javascript game