I'm making a primitive game with Javascipt for fun. I have set up functions to move my character around when the arrow keys are pressed as follows:
document.getElementById("character").style.top = 0;
document.getElementById("character").style.left = 0;
document.body.onkeydown = function() {
var e = event.keyCode,
charTop = parseInt(document.getElementById("character").style.top),
charLeft = parseInt(document.getElementById("character").style.left);
if (e == 40) { //down function
document.getElementById("character").style.top = (parseInt(document.getElementById("character").style.top)) + 10 + "px";
} else if (e == 37) { //left function
document.getElementById("character").style.left = (parseInt(document.getElementById("character").style.left)) - 10 + "px";
} else if (e == 39) { //right function
document.getElementById("character").style.left = (parseInt(document.getElementById("character").style.left)) + 10 + "px";
} else if (e == 38) { //up function
document.getElementById("character").style.top = (parseInt(document.getElementById("character").style.top)) - 10 + "px";
}
}
and it works, but not the way I'd like it to. I find that if you hold down an arrow key, it will move once, wait a second, then continuously move until you let go. I think what is causing this is Windows' built in function to repeat a pressed button at a speed set in Control Panel after a specified delay.
I would prefer that the character move immediately and continuously without the delay. I am not sure how to go about this. I'd also like to be able to define how quickly to move again if the button is still pressed (it moves ten pixels at a time, and if it repeated too quickly it would fly across the screen).
I know that Javascript is not really meant for game programming, but hopefully someone can think of a way around this! Thanks!
I'm making a primitive game with Javascipt for fun. I have set up functions to move my character around when the arrow keys are pressed as follows:
document.getElementById("character").style.top = 0;
document.getElementById("character").style.left = 0;
document.body.onkeydown = function() {
var e = event.keyCode,
charTop = parseInt(document.getElementById("character").style.top),
charLeft = parseInt(document.getElementById("character").style.left);
if (e == 40) { //down function
document.getElementById("character").style.top = (parseInt(document.getElementById("character").style.top)) + 10 + "px";
} else if (e == 37) { //left function
document.getElementById("character").style.left = (parseInt(document.getElementById("character").style.left)) - 10 + "px";
} else if (e == 39) { //right function
document.getElementById("character").style.left = (parseInt(document.getElementById("character").style.left)) + 10 + "px";
} else if (e == 38) { //up function
document.getElementById("character").style.top = (parseInt(document.getElementById("character").style.top)) - 10 + "px";
}
}
and it works, but not the way I'd like it to. I find that if you hold down an arrow key, it will move once, wait a second, then continuously move until you let go. I think what is causing this is Windows' built in function to repeat a pressed button at a speed set in Control Panel after a specified delay.
I would prefer that the character move immediately and continuously without the delay. I am not sure how to go about this. I'd also like to be able to define how quickly to move again if the button is still pressed (it moves ten pixels at a time, and if it repeated too quickly it would fly across the screen).
I know that Javascript is not really meant for game programming, but hopefully someone can think of a way around this! Thanks!
Share Improve this question asked Mar 18, 2015 at 9:50 Eric David SartorEric David Sartor 5972 gold badges8 silver badges22 bronze badges3 Answers
Reset to default 9This 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/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 plicated 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.
Try using onkeyup instead of onkeydown, onkeyup takes the value after the key is released
document.getElementById("character").style.top = 0;
document.getElementById("character").style.left = 0;
document.body.onkeyup = function() {
var e = event.keyCode,
charTop = parseInt(document.getElementById("character").style.top),
charLeft = parseInt(document.getElementById("character").style.left);
if (e == 40) { //down function
document.getElementById("character").style.top = (parseInt(document.getElementById("character").style.top)) + 10 + "px";
} else if (e == 37) { //left function
document.getElementById("character").style.left = (parseInt(document.getElementById("character").style.left)) - 10 + "px";
} else if (e == 39) { //right function
document.getElementById("character").style.left = (parseInt(document.getElementById("character").style.left)) + 10 + "px";
} else if (e == 38) { //up function
document.getElementById("character").style.top = (parseInt(document.getElementById("character").style.top)) - 10 + "px";
}
}
Create a global HOP variable which will define how fast your character moves.
onkeydown you create a:
var inter = setInterval(function(){
//apply movements here
}, HOP);
Then on keyup you stop the movement with:
clearInterval(inter);
The setInterval()
will call itself every HOP
( which is in milliseconds ). This will loop till you detect that the user has released the key.
To make sure that you don't create multiple setIntervals make sure you initialize globally your var inter = -1
and everytime you clear it you set it back to -1. This way when a keydown is detected.. just check to see if inter is > -1. If it is, then do nothing.
Simple demo here
HTML:
<div id="container"></div>
JS:
var container = document.getElementById('container');
var p = document.createElement('p');
var inter = -1;
var HOP = 50; //miliseconds
window.onkeydown = function(){
if(inter == -1){
inter = setInterval(function(){
var p_temp = p.cloneNode();
p_temp.innerHTML = "Key is down";
container.appendChild(p_temp);
}, HOP);
}
};
window.onkeyup = function(){
clearInterval(inter);
inter = -1;
};