This is a standard issue in lots of coding languages that rely on keyboard interrupts to control game characters, and isn't particular to JavaScript. The way around this is to trap the keys separately from your character's movement, and not rely on the keyboard event to redraw/update your game. Instead use a continual game loop that can, and will quite often, do nothing (many times a second) — until something in your game changes. Which seems a strange thing to do, but it is the way that many games are designed and built — even in JavaScript ;)
/// store key codes and currently pressed ones
var keys = {};
keys.LEFT = 37;
keys.RIGHT = 39;
/// store reference to character's position and element
var character = {
x: 100,
y: 100,
element: document.getElementById("character")
};
/// key detection (better to use addEventListener, but this will do)
document.body.onkeyup =
document.body.onkeydown = function(e){
var kc = e.keyCode || e.which;
keys[kc] = e.type == 'keydown';
};
/// character movement update
var moveCharacter = function(dx, dy){
character.x += dx||0;
character.y += dy||0;
character.element.style.left = character.x + 'px';
character.element.style.top = character.y + 'px';
};
/// character control
var detectCharacterMovement = function(){
if ( keys[keys.LEFT] ) {
moveCharacter(-1);
}
if ( keys[keys.RIGHT] ) {
moveCharacter(1);
}
};
/// game loop
setInterval(function(){
detectCharacterMovement();
}, 1000/24);
A live example (also with up and down movement):
/// store key codes and currently pressed ones
var keys = {};
keys.UP = 38;
keys.LEFT = 37;
keys.RIGHT = 39;
keys.DOWN = 40;
/// store reference to character's position and element
var character = {
x: 100,
y: 100,
speedMultiplier: 2,
element: document.getElementById("character")
};
/// key detection (better to use addEventListener, but this will do)
document.body.onkeyup =
document.body.onkeydown = function(e){
/// prevent default browser handling of keypresses
if (e.preventDefault) {
e.preventDefault();
}
else {
e.returnValue = false;
}
var kc = e.keyCode || e.which;
keys[kc] = e.type == 'keydown';
};
/// character movement update
var moveCharacter = function(dx, dy){
character.x += (dx||0) * character.speedMultiplier;
character.y += (dy||0) * character.speedMultiplier;
character.element.style.left = character.x + 'px';
character.element.style.top = character.y + 'px';
};
/// character control
var detectCharacterMovement = function(){
if ( keys[keys.LEFT] ) {
moveCharacter(-1, 0);
}
if ( keys[keys.RIGHT] ) {
moveCharacter(1, 0);
}
if ( keys[keys.UP] ) {
moveCharacter(0, -1);
}
if ( keys[keys.DOWN] ) {
moveCharacter(0, 1);
}
};
/// update current position on screen
moveCharacter();
/// game loop
setInterval(function(){
detectCharacterMovement();
}, 1000/24);
#character {
position: absolute;
width: 42px;
height: 42px;
background: red;
border-radius: 50%;
}
<div id="character"></div>
http://jsfiddle.net/7a106ck7/1/
The above is just the basics, there are a number of optimisations you can make. Nor is the above the be-all-and-end-all of controlling a game character, as sadly, keyboard events in browsers are rather unpredictable (has nothing really to do with JavaScript) and there are a number of issues you will find if you start trying to make more complicated control systems i.e. certain keys cannot be pressed simultaneously without problems arising (especially control characters like CMD or CTRL).
One day it would be great if JavaScript had a Keyboard object, one which behaved just as the keys
object above. Meaning you can test for the status of any key currently pressed, at any time, without having to rely on an event model. It may happen at some yet-unknown-future-point, but we'll have to wait and see.
If it does happen, I'd like to suggest a feature:
Keyboard.isPressed(Keyboard.ANY)
This would help in an untold number of game intro/menu screens (why do I have to press any key before I see the menu? Are we dreaming of arcade machines?). Not to mention the above would help out all those still waiting for the existence of what they deem a missing key.
keys[kc] = e.type == 'keydown'
which if you press the left arrow I assume this sets keys[37] totrue
. But then when you press the right arrow it would set keys[39] totrue
. Are they not now both set to true? In yourdetectCharacterMovement
function I would expect both of the if statements to be true because both left and right are nowtrue
. What am I misunderstanding about your code? – Radiothermy