I have the beginnings of a 2d Javascript game – at the moment the player can drive a triangle around the screen using the arrow keys.
The problem is that sometimes the triangle will get stuck rotating in one direction or moving forward until the corresponding control key is pressed again and the onkeyup event is fired again. This usually happens when more than one control key is pressed at the same time.
I can’t work out why it’s getting stuck in the first place unless the onkeyup events aren’t getting fired for some reason. Any help would be much appreciated, thank you.
Here’s some of the code, you can find a 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;
}
}
...
Problem description
The
keyupevents are fired, but sometimes a finalkeydownevent fires directly after thekeyup. Weird? Yes.The problem is the way how the repetition of the
keydownevent for a held key is implemented:the
keydownevent for the last held key is fired repeatedly. Due to the asynchronous, single-threaded nature of optimized JavaScript code execution, it can happen that sometimes such a repeatedkeydownevent is fired after the correspondingkeyupevent was invoked.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:
You can see three things in this example console output:
keydownevent for [RIGHT] (38) is fired repeatedly.keyupevent for [RIGHT] (38) is fired when i release the key.keydownevent is fired, after thekeyupwas executed.Thats the reason why your
rotationstate is "stuck" with value1even 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
Keyinto your code. See it working in this JSFiddle example.Now the console output looks like:
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
keydownevent basically looks like this:Update #1 Find an updated (fixed) version of your code on JSFiddle.