我开始了 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;
}
}
...
keyup
事件 会被触发,但有时最终的 keydown
事件会在 keyup
之后直接触发。诡异的?是的。
问题在于如何实现按住键的
keydown
事件重复的实现方式:最后一个按住的键的 keydown
事件被重复触发。由于优化的 JavaScript 代码执行的异步、单线程特性,有时可能会在调用相应的
keydown
事件之后触发此类重复的
keyup
事件。看看当我同时按住并释放 [UP] 和 [RIGHT] 键时会发生什么在我的 JSFiddle 示例中在 Windows 7 上使用 Firefox 13.0.1 观察到:
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
您可以在此示例控制台输出中看到三件事:
仅重复触发 [RIGHT] (38) 的
keydown
事件被触发。最后一个预定的
keyup
keydown
被执行。
这就是为什么即使没有按下任何键,您的keyup
rotation
。解决方案
我在您的代码中引入了一个辅助对象 1
在这个 JSFiddle 示例中查看它的工作原理。
现在控制台输出如下所示:Key
我的解决方案受到
这篇博客文章的启发,该文章更进一步,解释了如何避免在使用 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
更新 #1var 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); };
Set
键盘事件,并在按下键盘时使用
add
。我觉得使用
delete
字段来区分按键(而不是使用
event.key
)是一种很好的做法,以维护用户键盘上写入的内容与我的代码解释的内容之间的语义映射。 (例如,我相信 QWERTY 键盘的 W 会报告与 AZERTY 键盘的 Z 相同的
keyCode
,但没有法语键盘可供验证。)
这工作得很好(我没有看到其他人报告的无序事件的问题),直到修改器出现为止。我发现了两个问题:
...举行 Shift 的地方。如果用户的击键顺序为(hold-shift、hold-R、release-shift、release-R),则 JS 事件的顺序为 wrt。
keyCode
event.key
,但是释放了
R
,所以我的
r
的
Set
被卡住了。我的解决方案是将
R
转换为
Set
,其中键是
Map
,值是在 keydown 事件时报告的
event.keyCode
。这样,当我收到 keyup 事件时,我只需
event.key
对应于
delete
的地图键,并且按键不会被卡住。我希望选项/替代项需要相同的处理。
...Meta 键所在的位置(Mac 上的 Command;我猜 Windows 上的 Control?)。在这种情况下,执行的序列(hold-meta、hold-K、release-meta、release-K)将生成事件(keydown-meta、keydown-k、keyup-meta)。因此,K 的 keyup 事件确实丢失了,即使我确信会做
keyCode
哦,还有,关于模糊——我正在用核武器攻击地图。那么所有的赌注都结束了...