用另一个键按下时发生两次键事件
Key Event happening twice when pressed with another key
在我的游戏中,我使用事件侦听器 keyup
和 keydown
来跟踪按下了哪些键(this.input
是对下面 Input
函数的引用) :
$(document.body).on('keydown', this.input.keyPress.bind(this.input));
$(document.body).on('keyup', this.input.keyPress.bind(this.input));
一切都按预期进行,直到我同时按下 2 个键。左键将玩家向左移动,Shift 键开火。另外,它们工作正常。但是,如果同时按下并按住,则玩家会射击两次。
这是我用来处理输入的代码(为简洁起见删除了其他关键事件)
shootSpecial
应该只调用一次,因为 keyMap.shift
在 keydown
上只有 true
。一旦 keyup
被触发,它被设置为 false
:
keyMap[key] = e.type === 'keydown';
var Input = function () {
var keyMap = {};
this.handleKeyPress = function () {
if (keyMap.arrowleft === true) {
game.player.moving.left = true;
} else if (keyMap.shift === true) {
game.player.shootSpecial();
}
};
this.keyPress = function (e) {
var key = e.key.toLowerCase();
if (key === ' ') {
key = 'space';
}
keyMap[key] = e.type === 'keydown';
console.log(key, keyMap[key]);
this.handleKeyPress();
};
};
console.log
报告 left 和 shift 在 keydown
上 true
然后false
在 keyup
上,所以我不太确定为什么它会触发两次 shootSpecial
事件。
var keyMap = {};
var handleKeyPress = function () {
if (keyMap.arrowleft === true) {
console.log('left');
} else if (keyMap.shift === true) {
console.log('shift');
}
};
var keyPress = function (e) {
var key = e.key.toLowerCase();
keyMap[key] = e.type === 'keydown';
if (document.getElementById(key))
document.getElementById(key).innerText = e.type === 'keydown';
handleKeyPress();
}
document.addEventListener('keydown', keyPress);
document.addEventListener('keyup', keyPress);
LEFT:<div id="arrowleft">false</div>
SHIFT:<div id="shift">false</div>
正如您在 fiddle 中所见,一个事件被调用了两次。
您的问题是您使用相同的代码处理 keydown 和 keyup,结果是在按下 key2 的同时释放 key1 与按下 key2 的效果相同。
修复非常简单:根本不要在 keyup 事件上调用 handleKeyPress 函数。
var keyMap = {};
var handleKeyPress = function () {
if (keyMap.arrowleft === true) {
console.log('left');
} else if (keyMap.shift === true) {
console.log('shift');
}
};
var keyPress = function (e) {
var key = e.key.toLowerCase();
keyMap[key] = e.type === 'keydown';
if (document.getElementById(key))
document.getElementById(key).innerText = e.type === 'keydown';
// Only call handleKeyPress if the key is pressed
if (keyMap[key]) {
handleKeyPress();
}
}
document.addEventListener('keydown', keyPress);
document.addEventListener('keyup', keyPress);
LEFT:<div id="arrowleft">false</div>
SHIFT:<div id="shift">false</div>
添加一条建议以回应 OP 的评论。你的问题是你的 handleKeyPress() 没有关于键是被按下还是向上按下的信息,它也不知道甚至按下了哪个键,只知道在它被调用时哪些键是按下的。为了得到你的目标,我会做这样的事情:
var keyPress = function (e) {
var key = e.key.toLowerCase(),
isKeyDown = e.type === "keydown",
output = key + (isKeyDown ? "-down" : "-up") + ": ";
switch (key) {
case "shift":
if (isKeyDown) {
output += "pew pew";
} else {
output += "doing nothing"
}
break;
case "arrowleft":
output += "updating walking_left";
document.getElementById("walking_left").innerText = isKeyDown;
break;
default:
output += "doing nothing";
}
console.log(output);
};
document.addEventListener('keydown', keyPress);
document.addEventListener('keyup', keyPress);
Walking left:<div id="walking_left">false</div>
如果您按下两个键,您将触发两个事件,无论您是否同时按下它们。 keyup
事件也是如此。所以 handleKeypress 函数总共被调用了四次。
顺序可能恰好是:
key | event | keyMap.arrowleft | keyMap.shift | effect
-----------+---------+------------------+--------------+----------
shift | keydown | false | true | shoot
arrowleft | keydown | true | true | moving.left=true
arrowleft | keyup | false | true | shoot
shift | keyup | false | false | nothing
所以在那种情况下你射击了两次。当然,情况并非总是如此,因为顺序可能会有所不同。
解决方案
将键作为参数传递给 handleKeyPress 函数以确保您只执行与该键对应的操作。
var Input = function () {
var keyMap = {};
this.handleKeyPress = function (key) {
switch (key) {
case 'arrowleft':
game.player.moving.left = keyMap[key]; // also sets back to false
break;
case 'shift':
if (keyMap.shift) game.player.shootSpecial();
break;
}
};
this.keyPress = function (e) {
var key = e.key.toLowerCase();
if (key === ' ') {
key = 'space';
}
keyMap[key] = e.type === 'keydown';
this.handleKeyPress(key); // pass key
};
};
在我的游戏中,我使用事件侦听器 keyup
和 keydown
来跟踪按下了哪些键(this.input
是对下面 Input
函数的引用) :
$(document.body).on('keydown', this.input.keyPress.bind(this.input));
$(document.body).on('keyup', this.input.keyPress.bind(this.input));
一切都按预期进行,直到我同时按下 2 个键。左键将玩家向左移动,Shift 键开火。另外,它们工作正常。但是,如果同时按下并按住,则玩家会射击两次。
这是我用来处理输入的代码(为简洁起见删除了其他关键事件)
shootSpecial
应该只调用一次,因为 keyMap.shift
在 keydown
上只有 true
。一旦 keyup
被触发,它被设置为 false
:
keyMap[key] = e.type === 'keydown';
var Input = function () {
var keyMap = {};
this.handleKeyPress = function () {
if (keyMap.arrowleft === true) {
game.player.moving.left = true;
} else if (keyMap.shift === true) {
game.player.shootSpecial();
}
};
this.keyPress = function (e) {
var key = e.key.toLowerCase();
if (key === ' ') {
key = 'space';
}
keyMap[key] = e.type === 'keydown';
console.log(key, keyMap[key]);
this.handleKeyPress();
};
};
console.log
报告 left 和 shift 在 keydown
上 true
然后false
在 keyup
上,所以我不太确定为什么它会触发两次 shootSpecial
事件。
var keyMap = {};
var handleKeyPress = function () {
if (keyMap.arrowleft === true) {
console.log('left');
} else if (keyMap.shift === true) {
console.log('shift');
}
};
var keyPress = function (e) {
var key = e.key.toLowerCase();
keyMap[key] = e.type === 'keydown';
if (document.getElementById(key))
document.getElementById(key).innerText = e.type === 'keydown';
handleKeyPress();
}
document.addEventListener('keydown', keyPress);
document.addEventListener('keyup', keyPress);
LEFT:<div id="arrowleft">false</div>
SHIFT:<div id="shift">false</div>
正如您在 fiddle 中所见,一个事件被调用了两次。
您的问题是您使用相同的代码处理 keydown 和 keyup,结果是在按下 key2 的同时释放 key1 与按下 key2 的效果相同。
修复非常简单:根本不要在 keyup 事件上调用 handleKeyPress 函数。
var keyMap = {};
var handleKeyPress = function () {
if (keyMap.arrowleft === true) {
console.log('left');
} else if (keyMap.shift === true) {
console.log('shift');
}
};
var keyPress = function (e) {
var key = e.key.toLowerCase();
keyMap[key] = e.type === 'keydown';
if (document.getElementById(key))
document.getElementById(key).innerText = e.type === 'keydown';
// Only call handleKeyPress if the key is pressed
if (keyMap[key]) {
handleKeyPress();
}
}
document.addEventListener('keydown', keyPress);
document.addEventListener('keyup', keyPress);
LEFT:<div id="arrowleft">false</div>
SHIFT:<div id="shift">false</div>
添加一条建议以回应 OP 的评论。你的问题是你的 handleKeyPress() 没有关于键是被按下还是向上按下的信息,它也不知道甚至按下了哪个键,只知道在它被调用时哪些键是按下的。为了得到你的目标,我会做这样的事情:
var keyPress = function (e) {
var key = e.key.toLowerCase(),
isKeyDown = e.type === "keydown",
output = key + (isKeyDown ? "-down" : "-up") + ": ";
switch (key) {
case "shift":
if (isKeyDown) {
output += "pew pew";
} else {
output += "doing nothing"
}
break;
case "arrowleft":
output += "updating walking_left";
document.getElementById("walking_left").innerText = isKeyDown;
break;
default:
output += "doing nothing";
}
console.log(output);
};
document.addEventListener('keydown', keyPress);
document.addEventListener('keyup', keyPress);
Walking left:<div id="walking_left">false</div>
如果您按下两个键,您将触发两个事件,无论您是否同时按下它们。 keyup
事件也是如此。所以 handleKeypress 函数总共被调用了四次。
顺序可能恰好是:
key | event | keyMap.arrowleft | keyMap.shift | effect
-----------+---------+------------------+--------------+----------
shift | keydown | false | true | shoot
arrowleft | keydown | true | true | moving.left=true
arrowleft | keyup | false | true | shoot
shift | keyup | false | false | nothing
所以在那种情况下你射击了两次。当然,情况并非总是如此,因为顺序可能会有所不同。
解决方案
将键作为参数传递给 handleKeyPress 函数以确保您只执行与该键对应的操作。
var Input = function () {
var keyMap = {};
this.handleKeyPress = function (key) {
switch (key) {
case 'arrowleft':
game.player.moving.left = keyMap[key]; // also sets back to false
break;
case 'shift':
if (keyMap.shift) game.player.shootSpecial();
break;
}
};
this.keyPress = function (e) {
var key = e.key.toLowerCase();
if (key === ' ') {
key = 'space';
}
keyMap[key] = e.type === 'keydown';
this.handleKeyPress(key); // pass key
};
};