我有一个2D游戏的开头 - 玩家可以使用箭头键在屏幕上围绕一个三角形。
问题在于,有时三角形将在一个方向上旋转或向前移动,直到再次按下相应的控制键并再次触发onkeyup事件。这通常发生在同时按下多个控制键时。
除非onkeyup事件因某种原因没有被解雇,否则我无法解决为什么它会陷入困境。非常感谢任何帮助,谢谢。
这里有一些代码,你可以找到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;
}
}
...
答案 0 :(得分:11)
keyup
事件已被触发,但有时最终keydown
事件会在keyup
之后直接触发。奇怪的?是。
问题在于如何实现重复保持密钥的keydown
事件:
重复触发最后一个保持密钥的keydown
事件。由于优化的JavaScript代码执行的异步,单线程特性,有时会在触发相应的keydown
事件后触发这样的重复keyup
事件。
当我同时按住[UP]和[RIGHT]键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
您可以在此示例控制台输出中看到三件事:
keydown
事件。keyup
事件被触发。keydown
执行完之后, 即使没有按下任何键,也会导致keyup
状态“卡住”值为rotation
的原因。
为避免这种情况,您可以跟踪释放密钥时的微秒。当一个keydown事件发生只需几微秒时,我们就会默默地忽略它。
我在您的代码中引入了一个辅助对象1
。 See it working in this JSFiddle example.
现在控制台输出如下:
Key
我的解决方案受到this blog article的启发,它更进一步,并解释了如何避免使用JavaScript关键事件进行游戏开发时的草率和“单向”问题。这绝对值得一读。
忽略延迟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
事件的解决方法基本上如下所示:
keydown
更新#1 Find an updated (fixed) version of your code on JSFiddle。
答案 1 :(得分:2)
这个问题已经有将近8年的历史了,但是我遇到了类似的问题,并偶然发现了该页面,以防万一它对其他人有帮助:
我的代码中存在卡纸问题,该代码在键盘按下时使用Set
至add
键盘事件,在键盘按下时使用delete
。我觉得使用event.key
字段来区分按键是一种好习惯,而不是说keyCode
来维护用户键盘上所写内容与我的代码所解释内容之间的语义映射。 (例如,我相信QWERTY键盘的W将报告与AZERTY键盘的Z相同的keyCode
,但没有法语键盘可以进行验证。)
这很好用(我看不到其他人报告的乱序事件的问题),直到出现修饰符。我发现了两个问题:
event.key
就可以了(keydown-shift,keydown-R,keyup-shift,keyup-r)。换句话说,按下了R
,但是释放了r
,因此我的Set
陷入了R
的困境。我对此的解决方案是将Set
转换为Map
,其中键是event.keyCode
,值是event.key
,在按键事件的时间。这样,当我收到一个keyup事件时,我只需delete
对应于keyCode
的map键,键就不会卡住。我希望option / alt需要同样的处理。event.preventDefault
来防止系统窃取我的事件,也确实缺少K的keyup事件。我希望对此有一个更好的解决方案,但是当我看到Meta键事件时,我现在正在做的是整张地图。为了记录,我应该说我的代码实际上并没有使用Meta作为修饰符。我只想确保它的安全性,并且当用户快速键入内容时,我的密钥处理程序似乎很容易被卡住。哦,还有一点模糊-我正在对地图进行核对。然后所有赌注都关闭了...