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).
Since
keydownevents fire rapidly, you could store a timestamp noting when the last time you heard the key was depressed:Then, run an interval function to check for keys that are still registered as depressed but have not sent a
keydownevent 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.Ais now an array instead of a boolean). Instead, you could hold the timestamps in their own object:Have the interval function check the members of
last_keydown_times(perhaps in afor...inloop) and check for expired values: