admin管理员组

文章数量:1420086

Working on a small game using an HTML5 canvas, and javascript. And it's working quite nicely, apart from keyboard keys occasionally getting "stuck", ie. it'll register a keydown event, but sometimes it won't register the keyup event. This generally happens when hitting multiple keys at once, so it is in part a hardware or OS fault, but I'm hoping someone has ideas on how to work around this. The relevant parts of my code (written in coffeescript):

press = (direction) ->
    switch direction
        when 'W'
            game.keys.W = true
        when 'S'
            game.keys.S = true
        when 'A'
            game.keys.A = true
        when 'D'
            game.keys.D = true


release = (direction) ->
    switch direction
        when 'W'
            game.keys.W = false
        when 'S'
            game.keys.S = false
        when 'A'
            game.keys.A = false
        when 'D'
            game.keys.D = false

doKeyDown = (evt) ->
    switch evt.keyCode
        when 87 then press 'W'
        when 83 then press 'S'
        when 65 then press 'A'
        when 68 then press 'D'

doKeyUp = (evt) ->
    switch evt.keyCode
        when 87 then release 'W'
        when 83 then release 'S'
        when 65 then release 'A'
        when 68 then release 'D'

window.addEventListener('keydown', doKeyDown, false)

window.addEventListener('keyup', doKeyUp, false)

Basic stuff. It wouldn't be a problem if javascript wasn't entirely event-driven, but what can you do. My current idea for how to circumvent the problem would be to add a queue for keypresses, with a capacity for 2 or 3 keys, and when the limit is exceeded, the first key(s) in are removed and are switched to 'false'. Still, it seems weird to add an artificial limit like this, when it should be avoidable!

So, the question is, is there a straightforward way to ensuring key states are saved accurately, without having to resort to workarounds? Preferably something that works independently of the framerate of the game (should work equally well whether it's running at 1000 fps, or 3 fps).

Working on a small game using an HTML5 canvas, and javascript. And it's working quite nicely, apart from keyboard keys occasionally getting "stuck", ie. it'll register a keydown event, but sometimes it won't register the keyup event. This generally happens when hitting multiple keys at once, so it is in part a hardware or OS fault, but I'm hoping someone has ideas on how to work around this. The relevant parts of my code (written in coffeescript):

press = (direction) ->
    switch direction
        when 'W'
            game.keys.W = true
        when 'S'
            game.keys.S = true
        when 'A'
            game.keys.A = true
        when 'D'
            game.keys.D = true


release = (direction) ->
    switch direction
        when 'W'
            game.keys.W = false
        when 'S'
            game.keys.S = false
        when 'A'
            game.keys.A = false
        when 'D'
            game.keys.D = false

doKeyDown = (evt) ->
    switch evt.keyCode
        when 87 then press 'W'
        when 83 then press 'S'
        when 65 then press 'A'
        when 68 then press 'D'

doKeyUp = (evt) ->
    switch evt.keyCode
        when 87 then release 'W'
        when 83 then release 'S'
        when 65 then release 'A'
        when 68 then release 'D'

window.addEventListener('keydown', doKeyDown, false)

window.addEventListener('keyup', doKeyUp, false)

Basic stuff. It wouldn't be a problem if javascript wasn't entirely event-driven, but what can you do. My current idea for how to circumvent the problem would be to add a queue for keypresses, with a capacity for 2 or 3 keys, and when the limit is exceeded, the first key(s) in are removed and are switched to 'false'. Still, it seems weird to add an artificial limit like this, when it should be avoidable!

So, the question is, is there a straightforward way to ensuring key states are saved accurately, without having to resort to workarounds? Preferably something that works independently of the framerate of the game (should work equally well whether it's running at 1000 fps, or 3 fps).

Share Improve this question edited Jun 8, 2012 at 13:02 TJHeuvel 12.6k4 gold badges39 silver badges46 bronze badges asked Jun 8, 2012 at 13:00 FaultFault 4203 silver badges17 bronze badges 4
  • Can't you write game.keys[direction] = true in coffeescript? It'd save some typing ... – Pointy Commented Jun 8, 2012 at 13:02
  • also press String.fromCharCode(evt.keyCode) – Pointy Commented Jun 8, 2012 at 13:03
  • I could indeed! Thanks for pointing that out. Still new to javascript, or, well, scripting languages as a whole, so I end up missing a lot of the nice ways in which things can be optimized/condensed. – Fault Commented Jun 8, 2012 at 13:17
  • I've never heard of a work-around for this. If the events aren't being delivered after binding them to the window, then you're either right that it's an OS bug, or the window just doesn't have focus at the time of keyup. The same issue can be observed even in native SDL applications. – bkconrad Commented Jun 8, 2012 at 13:28
Add a ment  | 

4 Answers 4

Reset to default 3

Since keydown events fire rapidly, you could store a timestamp noting when the last time you heard the key was depressed:

when 'A'
    game.keys.A = [true, +newDate()]

Then, run an interval function to check for keys that are still registered as depressed but have not sent a keydown event for some duration (say, 1 second) and set them as released.

My above example might require you to refactor a lot of your code (since game.keys.A is now an array instead of a boolean). Instead, you could hold the timestamps in their own object:

last_keydown_times = {};
...

when 'A'
    game.keys.A = true
    last_keydown_times['A'] = +new Date();

Have the interval function check the members of last_keydown_times (perhaps in a for...in loop) and check for expired values:

setInterval(function() {
    var now = +new Date();
    for(key in last_keydown_times) {
        if(now - last_keydown_times[key] >= 1000) {
            // run the release code for this key
        }
    }
}, 1000);

If this is happening on OSX, be aware that you simply don't get any keyup events while the cmd key is held down.

AFAICT there is no solution to this problem. The only way to track which keys are currently down is through keyup/keydown events, and on a Mac the cmd key robs you of this information.

It's worth pointing out that having more keydown events than keyup events is actually expected behavior: When you hold a key down, the OS reports that as multiple keydowns (one for each time that a character would be inserted in a normal text input, though this happens even under Mac OS 10.7 with its default key-holding behavior). You'll only get a keyup when the key is physically released.

Of course, your code shouldn't be affected by this issue. So if you're really in a situation where game.keys.A is true well after you've released the A key, you should report that as a bug in the browser(s) you're experiencing the problem in. I don't believe a workaround to be possible.

This is a mon issue with all but the more expensive keyboards.

As an example, try playing a game where you have the arrow keys to move and Space to fire. You'll find that pressing two arrows may result in you being unable to shoot.

Unfortunately there isn't anything you can do to fix this, because a lot of keyboards are different. What you should do is allow the user to customise their controls in case they encounter the problem, so they can change to a set of controls that doesn't have this issue.

本文标签: keyboardJavascript quotstuckquot keys (unregistered keyup event)Stack Overflow