在 Javascript 游戏中同时处理 KeyEvents
Handling simultaneous KeyEvents in a Javascript Game
我正在 Javascript 开发一款角色扮演游戏,正在为主要玩家设置控件,但我在处理 keyEvent 时遇到了一些问题。此代码控制玩家移动和动画。哪里出了问题,我相信,如果游戏在已经有另一个按键的情况下注册了一个按键,然后第一个按键上升,精灵暂停(isMoving 属性 基本上停止移动和动画,当它是假)。
// For example:
// I hold W. Sprite begins moving up.
// While holding W, I begin to hold D. Sprite begins moving right.
// I release W. Sprite should keep going right, but pauses momentarily when the
// keyup is registered
var move = function(e) {
xavier.isMoving = true;
switch(e.keyCode)
{
case 87: xavier.direction = 'up'; break; //W
case 65: xavier.direction = 'left'; break; //A
case 68: xavier.direction = 'right'; break; //D
case 83: xavier.direction = 'down'; break; //S
};
};
var wait = function(e) {
xavier.isMoving = false;
};
window.addEventListener("keydown", move, false);
window.addEventListener("keyup", wait, false);
是否有更好的方法来设置控件以便我的游戏可以同时处理多个按键?
此外,按下 mac 命令键时精灵会移动。不知道为什么。
当然有
您需要一个数组和 2 个事件侦听器,一个用于 keydown
,一个用于 keyup
。
每当有人按下按钮时,将id与按钮的keyCode相同的元素设置为true。如果您的数组名为 keys
,那么它看起来像这样:
keys[e.keyCode] = true;
然后,每当用户释放一个按钮时,将与该按钮的keyCode具有相同id的元素设置为false:
keys[e.keyCode] = false;
稍后,您可以检查与您希望在按下后发生特定事件的按钮具有相同 id 的元素是否为真。
var move = function(e) {
xavier.isMoving = true;
switch(e.keyCode)
{
case 87: xavier.direction.up = true; break; //W
case 65: xavier.direction.left = true; break; //A
case 68: xavier.direction.right = true; break; //D
case 83: xavier.direction.down = true; break; //S
};
};
var wait = function(e) {
switch(e.keyCode)
{
case 87: xavier.direction.up = true; break; //W
case 65: xavier.direction.left = true; break; //A
case 68: xavier.direction.right = true; break; //D
case 83: xavier.direction.down = true; break; //S
};
xavier.isMoving = xavier.direction.up ||
xavier.direction.left || xavier.direction.right ||
xavier.direction.down;
};
window.addEventListener("keydown", move, false);
window.addEventListener("keyup", wait, false);
这是一个解决方案,可以跟踪按下的键(方向)并在释放第二个键时返回到另一个按下的键(方向)。而且这两个功能非常相似,所以合二为一:
var xavier = {};
xavier.isMoving = false;
xavier.stack = [];
xavier.direction = '';
xavier.directions = {
87: 'up', //W
65: 'left', //A
68: 'right', //D
83: 'down', //S
};
var move = function(e) {
// get direction from mapping: key -> direction
var direction = xavier.directions[e.keyCode];
if (!direction) return; // not a move-key
var i = xavier.stack.indexOf(direction);
if (e.type === 'keydown' && i === -1) {
// If this key was not yet down, but is pressed,
// then add the direction to the list
xavier.stack.push(direction);
} else if (e.type === 'keyup' && i !== -1) {
// If this key was down, but is released,
// then remove the direction from the list
xavier.stack.splice(i, 1);
}
xavier.direction = xavier.stack.length ? xavier.stack[xavier.stack.length-1]
: '';
xavier.isMoving = xavier.direction !== '';
// Test for this snippet only:
document.body.textContent = xavier.direction;
};
window.addEventListener("keydown", move, false);
window.addEventListener("keyup", move, false);
Click here (to focus) and press/release ASQW keys
请注意,理论上这支持同时按下任意数量的键,但实际上键盘通常限制为可以按下的数量。一旦超过此限制,将不会生成任何按键事件。
您需要取消关键事件与动画或主游戏循环的关联。 keydown, keyup 事件是 IO 事件,不应该处理游戏逻辑,它们可以以任何速率进入,你可能在帧之间有很多,并且在每个帧上应用逻辑只是浪费处理,如果你有更多的逻辑,更糟糕的是玩家可能会错过逻辑事件,因为关键事件在主循环没有处理新位置的情况下已经进行。鼠标和触摸事件也是如此,你应该只记录事件并在游戏内处理。
// define keys and an array to keep key states
// global key log;
var keyState = [];
const KEY_UP = 38;
const KEY_DOWN = 40;
const KEY_LEFT = 37;
const KEY_RIGHT = 39;
// create a logging function
const keyEventLogger = function (e) { keyState[e.keyCode] = e.type == 'keydown';}
window.addEventListener("keydown", keyEventLogger);
window.addEventListener("keyup", keyEventLogger);
// define something to move
var player = {y : 100, x: 100};
const MOVE_SPEED = 2;
// in the main loop;
function update(timer) {
if (keyState[KEY_UP]) {
player.y -= MOVE_SPEED;
}
if (keyState[KEY_DOWN]) {
player.y += MOVE_SPEED;
}
if (keyState[KEY_LEFT]) {
player.x -= MOVE_SPEED;
}
if (keyState[KEY_RIGHT]) {
player.x += MOVE_SPEED;
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
我正在 Javascript 开发一款角色扮演游戏,正在为主要玩家设置控件,但我在处理 keyEvent 时遇到了一些问题。此代码控制玩家移动和动画。哪里出了问题,我相信,如果游戏在已经有另一个按键的情况下注册了一个按键,然后第一个按键上升,精灵暂停(isMoving 属性 基本上停止移动和动画,当它是假)。
// For example:
// I hold W. Sprite begins moving up.
// While holding W, I begin to hold D. Sprite begins moving right.
// I release W. Sprite should keep going right, but pauses momentarily when the
// keyup is registered
var move = function(e) {
xavier.isMoving = true;
switch(e.keyCode)
{
case 87: xavier.direction = 'up'; break; //W
case 65: xavier.direction = 'left'; break; //A
case 68: xavier.direction = 'right'; break; //D
case 83: xavier.direction = 'down'; break; //S
};
};
var wait = function(e) {
xavier.isMoving = false;
};
window.addEventListener("keydown", move, false);
window.addEventListener("keyup", wait, false);
是否有更好的方法来设置控件以便我的游戏可以同时处理多个按键?
此外,按下 mac 命令键时精灵会移动。不知道为什么。
当然有
您需要一个数组和 2 个事件侦听器,一个用于 keydown
,一个用于 keyup
。
每当有人按下按钮时,将id与按钮的keyCode相同的元素设置为true。如果您的数组名为 keys
,那么它看起来像这样:
keys[e.keyCode] = true;
然后,每当用户释放一个按钮时,将与该按钮的keyCode具有相同id的元素设置为false:
keys[e.keyCode] = false;
稍后,您可以检查与您希望在按下后发生特定事件的按钮具有相同 id 的元素是否为真。
var move = function(e) {
xavier.isMoving = true;
switch(e.keyCode)
{
case 87: xavier.direction.up = true; break; //W
case 65: xavier.direction.left = true; break; //A
case 68: xavier.direction.right = true; break; //D
case 83: xavier.direction.down = true; break; //S
};
};
var wait = function(e) {
switch(e.keyCode)
{
case 87: xavier.direction.up = true; break; //W
case 65: xavier.direction.left = true; break; //A
case 68: xavier.direction.right = true; break; //D
case 83: xavier.direction.down = true; break; //S
};
xavier.isMoving = xavier.direction.up ||
xavier.direction.left || xavier.direction.right ||
xavier.direction.down;
};
window.addEventListener("keydown", move, false);
window.addEventListener("keyup", wait, false);
这是一个解决方案,可以跟踪按下的键(方向)并在释放第二个键时返回到另一个按下的键(方向)。而且这两个功能非常相似,所以合二为一:
var xavier = {};
xavier.isMoving = false;
xavier.stack = [];
xavier.direction = '';
xavier.directions = {
87: 'up', //W
65: 'left', //A
68: 'right', //D
83: 'down', //S
};
var move = function(e) {
// get direction from mapping: key -> direction
var direction = xavier.directions[e.keyCode];
if (!direction) return; // not a move-key
var i = xavier.stack.indexOf(direction);
if (e.type === 'keydown' && i === -1) {
// If this key was not yet down, but is pressed,
// then add the direction to the list
xavier.stack.push(direction);
} else if (e.type === 'keyup' && i !== -1) {
// If this key was down, but is released,
// then remove the direction from the list
xavier.stack.splice(i, 1);
}
xavier.direction = xavier.stack.length ? xavier.stack[xavier.stack.length-1]
: '';
xavier.isMoving = xavier.direction !== '';
// Test for this snippet only:
document.body.textContent = xavier.direction;
};
window.addEventListener("keydown", move, false);
window.addEventListener("keyup", move, false);
Click here (to focus) and press/release ASQW keys
请注意,理论上这支持同时按下任意数量的键,但实际上键盘通常限制为可以按下的数量。一旦超过此限制,将不会生成任何按键事件。
您需要取消关键事件与动画或主游戏循环的关联。 keydown, keyup 事件是 IO 事件,不应该处理游戏逻辑,它们可以以任何速率进入,你可能在帧之间有很多,并且在每个帧上应用逻辑只是浪费处理,如果你有更多的逻辑,更糟糕的是玩家可能会错过逻辑事件,因为关键事件在主循环没有处理新位置的情况下已经进行。鼠标和触摸事件也是如此,你应该只记录事件并在游戏内处理。
// define keys and an array to keep key states
// global key log;
var keyState = [];
const KEY_UP = 38;
const KEY_DOWN = 40;
const KEY_LEFT = 37;
const KEY_RIGHT = 39;
// create a logging function
const keyEventLogger = function (e) { keyState[e.keyCode] = e.type == 'keydown';}
window.addEventListener("keydown", keyEventLogger);
window.addEventListener("keyup", keyEventLogger);
// define something to move
var player = {y : 100, x: 100};
const MOVE_SPEED = 2;
// in the main loop;
function update(timer) {
if (keyState[KEY_UP]) {
player.y -= MOVE_SPEED;
}
if (keyState[KEY_DOWN]) {
player.y += MOVE_SPEED;
}
if (keyState[KEY_LEFT]) {
player.x -= MOVE_SPEED;
}
if (keyState[KEY_RIGHT]) {
player.x += MOVE_SPEED;
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);