I am working with keyboard events in Javascript. My goal is to be able to move images on a canvas with the keyboard arrows, but there is a problem: when I press two keys simultaneously and then release one of them, the key I left pressed does not work, it crashes and therefore the image It does not move. I'm already using the keyup and keydown events and I managed to find a way to detect when two keys are pressed at the same time, however I can't solve this problem. Can anyone help me? Thank you.
this is what i tried
function keyPressed(event) {
keysPressed[event.key] = true;
switch(event.key) {
case 'ArrowLeft':
playerPetSelected.speedX = -5;
break;
case 'ArrowRight':
playerPetSelected.speedX = 5;
break;
case 'ArrowDown':
playerPetSelected.speedY = 5;
break;
case 'ArrowUp':
playerPetSelected.speedY = -5;
break;
}
if (keysPressed['ArrowLeft'] && event.key == 'ArrowDown') {
playerPetSelected.speedX = -3.5;
playerPetSelected.speedY = 3.5;
}
if (keysPressed['ArrowDown'] && event.key == 'ArrowLeft') {
playerPetSelected.speedY = 3.5;
playerPetSelected.speedX = -3.5;
}
if (keysPressed['ArrowLeft'] && event.key == 'ArrowUp') {
playerPetSelected.speedX = -3.5;
playerPetSelected.speedY = -3.5;
}
if (keysPressed['ArrowUp'] && event.key == 'ArrowLeft') {
playerPetSelected.speedY = -3.5;
playerPetSelected.speedX = -3.5;
}
if (keysPressed['ArrowUp'] && event.key == 'ArrowRight') {
playerPetSelected.speedY = -3.5;
playerPetSelected.speedX = 3.5;
}
if (keysPressed['ArrowRight'] && event.key == 'ArrowUp') {
playerPetSelected.speedX = 3.5;
playerPetSelected.speedY = -3.5;
}
if (keysPressed['ArrowDown'] && event.key == 'ArrowRight') {
playerPetSelected.speedY = 3.5;
playerPetSelected.speedX = 3.5;
}
if (keysPressed['ArrowRight'] && event.key == 'ArrowDown') {
playerPetSelected.speedX = 3.5;
playerPetSelected.speedY = 3.5;
}
}
function stopMovement(event) {
keysPressed[event.key] = false;
playerPetSelected.speedX = 0;
playerPetSelected.speedY = 0;
}
The parts where I modify the 'playerPetSelected' object are so that the images can be moved on the canvas.
I was hoping I wouldn't have this problem anymore because here I was able to find a way to detect when two keys are pressed at the same time, but it only helped me to manipulate the speed of movement on the canvas when two keys are pressed simultaneously.
Here is how I would do it... Using an object for the speed in x/y. Look how the conditions are easier to read. That handles all possible arrow combinations.
window.addEventListener("keydown", keyPressed)
window.addEventListener("keyup", keyUnpressed)
let keysPressed = {}
function keyPressed(event) {
keysPressed[event.key] = true
getActiveKeys()
}
function keyUnpressed(event) {
keysPressed[event.key] = false
getActiveKeys()
}
function getActiveKeys() {
const activeKeys = Object.keys(keysPressed).map((key) => keysPressed[key] ? key : null).filter((key) => key)
const speed = {
x: 0,
y: 0
}
if(keysPressed["ArrowUp"]){
speed.y = 5
}
if(keysPressed["ArrowDown"]){
speed.y = -5
}
if(keysPressed["ArrowLeft"]){
speed.x = -5
}
if(keysPressed["ArrowRight"]){
speed.x = 5
}
// Gas and break conditions
if(keysPressed["ArrowUp"] && keysPressed["ArrowDown"]){
speed.y = 0
}
if(keysPressed["ArrowLeft"] && keysPressed["ArrowRight"]){
speed.x = 0
}
// Diagonal condition
if(speed.x && speed.y){
speed.x *= 0.7
speed.y *= 0.7
}
document.querySelector("#activeKeys").innerText = JSON.stringify(activeKeys)
document.querySelector("#xSpeed").innerText = speed.x
document.querySelector("#ySpeed").innerText = speed.y
}
<div>Keys pressed: <span id="activeKeys">[]</span></div>
<div>Speed X: <span id="xSpeed">0</span></div>
<div>Speed Y: <span id="ySpeed">0</span></div>
Run that snippet in full page mode, else the page will scroll. Or look at CodePen
I am trying to move a sprite using arrow keys. Below is the logic that SHOULD cause the sprite to move diagonally if two keys are pressed. However, that is not the case. It will only move in one direction a a time. Since I am saving each keyCode as a Boolean within an object property whenever I press a button, I would think that this should not be an issue, but I can't get it to work. I have also tried using an array to store these Booleans, and checking those values instead. No luck.
I am at my wit's end and would greatly appreciate some help. I've scoured stackoverflow and many different blogs on building 2d games, but but nothing that I try works.
var keyState = {};
window.onkeydown = window.onkeyup = function(e) {
keyState[e.keyCode] = e.type == 'keydown';
// checks for up and left
if (keyState[38] && keyState[37]) {
player.pos.x -= 2;
player.pos.y -= 2;
}
// checks for up and right
else if (keyState[38] && keyState[39]) {
player.pos.x += 2;
player.pos.y += 2;
}
// checks for down and left
else if (keyState[40] && keyState [37]) {
player.pos.x -= 2;
player.pos.y -= 2;
}
// checks for down and right
else if(keyState[40] && keyState [39]) {
player.pos.x += 2;
player.pos.y -= 2;
}
// checks for up
else if (keyState[38]) {
player.animation.y = 64;
player.pos.y -= 2;
}
};
You should explicitly set the object key values as booleans and you should toggle them in separate keydown/keyup handlers:
let keysdown = {};
window.addEventListener('keydown', function(evt) {
keysdown[evt.which] = true;
if (keysdown["38"] === true && keysdown["37"] === true) {
console.log('up & left');
} else if (keysdown["38"] === true && keysdown["39"] === true) {
console.log('up & right');
} else if (keysdown["40"] === true && keysdown["37"] === true) {
console.log('down and left');
} else if (keysdown["40"] === true && keysdown["39"] === true) {
console.log('down and right');
} else if (keysdown["38"] === true) {
console.log('up');
}
}, false);
window.addEventListener('keyup', function(evt) {
keysdown[evt.which] = false;
}, false);
This logs all the correct key combinations for me.
You need to move the movement logic out of the event listener. Have the key event just log the current state of each key (up or down) then in the main loop check the state and move the play as needed.
As I am unsure if you want the movement to be constant or only on the press I have included the option to change the behaviour with the constant flag MOVE_ONLY_ON_PRESS if true movement only when a press is first detected. So that no key strokes are missed I clear the key flag in the main loop.
The following is an example of how to interface with the keyboard for a game.
// global key log;
var keyState = [];
const KEY_UP = 38;
const KEY_DOWN = 40;
const KEY_LEFT = 37;
const KEY_RIGHT = 39;
// Player
var player = {};
player.x = 100;
player.y = 100;
const MOVE_SPEED = 2;
const MOVE_ONLY_ON_PRESS = false; // This will toggle constant movement or only on press
// from your code
window.onkeydown = window.onkeyup = function (e) {
if (MOVE_ONLY_ON_PRESS) {
if (e.type === 'keydown') {
keyState[e.keyCode] = true;
}
} else {
keyState[e.keyCode] = e.type == 'keydown';
}
}
// in the main loop;
function update(timer) {
// you dont need to test for the combinations (ie up left) when its just simple movement
if (keyState[KEY_UP]) {
player.y -= MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_UP] = false; }
}
if (keyState[KEY_DOWN]) {
player.y += MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_DOWN] = false; }
}
if (keyState[KEY_LEFT]) {
player.x -= MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_LEFT] = false; }
}
if (keyState[KEY_RIGHT]) {
player.x += MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_RIGHT] = false; }
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
Bit fields.
Another way to simplify the arrow keys is to set the first 4 bits of a number to correspond to a key, one bit per key, then it is easy to test for combinations of keys as each of the 16 possible combinations has a unique number. You Could map many more keys this way but it becomes a little impractical when you go to far.
This is how to set them
var arrowBits = 0; // the value to hold the bits
const KEY_BITS = [4,1,8,2]; // left up right down
const KEY_MASKS = [0b1011,0b1110,0b0111,0b1101]; // left up right down
window.onkeydown = window.onkeyup = function (e) {
if(e.keyCode >= 37 && e.keyCode <41){
if(e.type === "keydown"){
arrowKeys |= KEY_BITS[e.keyCode - 37];
}else{
arrowKeys &= KEY_MASKS[e.keyCode - 37];
}
}
}
These are 8 of the 16 possible combinations
// postfix U,D,L,R for Up down left right
const KEY_U = 1;
const KEY_D = 2;
const KEY_L = 4;
const KEY_R = 8;
const KEY_UL = KEY_U + KEY_L; // up left
const KEY_UR = KEY_U + KEY_R; // up Right
const KEY_DL = KEY_D + KEY_L; //
const KEY_DR = KEY_D + KEY_R; //
This is how you test for then
if ((arrowBits & KEY_UL) === KEY_UL) { // is UP and LEFT only down
if (arrowBits & KEY_UL) { // is Up left down (down and right may also be down)
if ((arrowBits & KEY_U) === KEY_U) { // is Up only down
if (arrowBits & KEY_U) { // is Up down (any other key may be down)
if (!(arrowBits & KEY_U)) { // is Up up (any other key may be down)
if (!arrowBits) { // no keys are down
if (arrowBits) { // any one or more keys are down
I cannot tell you why your code doesn't work. If I replace the player-movements with console-logs, it works for me.
Although it's pretty primitive.
First separate the keystate-manager from the movement-logic.
var isKeyDown = (function(alias){
for(var i=0, a=Object.create(null); i<256;) a[i++]=false;
for(var k in alias) i=0|alias[k], i>0 && i<256 && Object.defineProperty(a,k,{get:Function("return this["+i+"]")});
function keyStateHandler(e){
a[e.which||e.keyCode] = e.type==="keydown"
}
addEventListener("keydown", keyStateHandler, false);
addEventListener("keyup", keyStateHandler, false);
return a;
})({
//add some aliases for more readable code
up: 38,
left: 37,
right: 39,
down: 40
});
once, because the update-interval of the keydown-event is not reliable, varies between browsers, and is not sync with the update-interval of the brwoser wich will cause flickering.
Now create a main loop for your game, and take account for the timing. Although requestAnimationFrame aims for a constant framerate you may have lags, and you don't want to see your player move at different speeds during these lags.
var lastCall = Date.now();
function tick(){
requestAnimationFrame(tick);
var now = Date.now(),
time = (now - lastCall) / 1000; //time in seconds
//because it's easier to think in terms like 2px/s
//than 0.002px/ms or 0.0333 px/frame
lastCall = now;
move(time);
}
tick();
now the actual movement:
//the speed of your player: it would be better if this would be
//a property of the player than hardcoded like you did, or global like this
var speed = 2; // 2px/second
function move( time ){
//accounts for varying framerates
//opposite directions cancel each other out
var dx = (isKeyDown.right - isKeyDown.left) * time,
dy = (isKeyDown.down - isKeyDown.up) * time;
//taking account for diagonals
if(dx && dy) dx /= Math.SQRT2, dy /= Math.SQRT2;
player.pos.x += dx * speed;
player.pos.y += dy * speed;
}
Based on skyline3000 answer I tried to make it work also in down, left and right. Appart from adding the other options I had to change window to document to make it work:
let keysdown = {};
document.addEventListener('keydown', function (evt) {
keysdown[evt.which] = true;
if (keysdown["38"] === true && keysdown["37"] === true) {
prota.moveUp();
prota.moveLeft();
// console.log('up & left');
} else if (keysdown["38"] === true && keysdown["39"] === true) {
prota.moveUp();
prota.moveRight();
// console.log('up & right');
} else if (keysdown["40"] === true && keysdown["37"] === true) {
prota.moveDown();
prota.moveLeft();
// console.log('down and left');
} else if (keysdown["40"] === true && keysdown["39"] === true) {
prota.moveDown();
prota.moveRight();
// console.log('down and right');
} else if (keysdown["38"] === true) {
prota.moveUp();
// console.log('up');
} else if (keysdown["40"] === true) {
prota.moveDown();
// console.log('down');
} else if (keysdown["39"] === true) {
prota.moveRight();
// console.log('right');
} else if (keysdown["37"] === true) {
prota.moveLeft();
// console.log('left');
}
}, false);
document.addEventListener('keyup', function (evt) {
keysdown[evt.which] = false;
}, false);
window.addEventListener("keydown", keyDown, false);
window.addEventListener("keyup", keyUp, false);
var key = new Set();
function keyDown(k) {
key.add(k.keyCode);
if (key.has(83)) {
player.vy = 1;
};
if (key.has(87)) {
player.vy = -1;
};
if (key.has(65)) {
player.vx = -1;
if (key.has(68)) {
player.vx = -1;
};
};
if (key.has(68)) {
player.vx = 1;
if (key.has(65)) {
player.vx = 1;
};
};
};
function keyUp(k) {
key.delete(k.keyCode);
if (k.keyCode === 83) {
player.vy = 0;
};
if (k.keyCode === 87) {
player.vy = 0;
};
if (k.keyCode === 65) {
if (!key.has(68)) {
player.vx = 0;
};
};
if (k.keyCode === 68) {
if (!key.has(65)) {
player.vx = 0;
};
};
};
I am trying to create smooth movement when dialing around the W,A,S,D keys. I finally got something that works except one minor glitch. Whenever I am holding D to move right and I press A, the player's movement is not interrupted. That is how I want it to work but when I do the opposite, the movement is interrupted and reversed. If I switch the 2 blocks I pointed out in the code it will reverse the problem, fixing one and breaking the other. I assume its something to do with how its catching the if statements but I don't know. Can someone tell me what the ffffffff is going on?
I'm making a pacman game. If you've ever played it you'll know that when pacman is moving a certain direction through a maze, if he moves towards a wall nothing happens. He just keeps going. What would be a a good way of doing this given my current code?
So here's the code.
this.move = function(key){
var Rblocked = false;
var Bblocked = false;
clearInterval(timer);
timer = window.setInterval(function(){
//key is event switch
switch (key) {
case 39:
for (var butt39 in blockArrayLeft){
if ($('#pacMan').position().left == blockArrayLeft[butt39].position().left && $('#pacMan').position().top == blockArrayLeft[butt39].position().top){
Rblocked = true;
pacMan.move(this.direction);
}
}
if (!Rblocked){
this.direction = 39;
pacMan.rightMove();
}
break;
case 40:
for (var butt40 in blockArrayTop){
if ($('#pacMan').position().left == blockArrayTop[butt40].position().left && $('#pacMan').position().top == blockArrayTop[butt40].position().top){
Bblocked = true;
pacMan.move(this.direction);
}
}
if (!Bblocked){
this.direction = 40;
pacMan.bottomMove();
}
break;
default:
clearInterval(timer);
break;
}
}
};
}
Pressing a key causes pacMan to move one direction continuously. I have a setInterval that makes him move his length's space, every 100 seconds.
If he bumps into a wall and he moves towards it he is blocked. The setInterval only goes when the blocked variable is false.
But what would be a way to make him keep moving a direction if he goes right suddenly?
Space Invader game: I want to control the 'base gun' (move it left and right and fire missiles at the invaders. So I need a keypress or (keydown?) event to change a variable (x coordinate) and a key press event to fire a missile.
Can anyone show me how the keypress event is detected and the variable is changed?
document.onkeydown = function(e) {
var key = e.keyCode;
if (key===37) {//left arrow pressed
} else if (key===39) {//right arrow pressed
}
}
Like this?
document.onkeydown = checkKey;
var xCoord = 100;
function checkKey(e) {
e = e || window.event;
switch (e.keyCode) {
case 37 : // left
xCoord -= 5;
break;
case 39 : // right
xCoord += 5;
break;
}
}
Exciting fiddle: http://jsfiddle.net/u5eJp/
Couple things I would like to add to the other answers:
1) Use constants to make it easier on yourself
2) There is no way to check if a key is currently pressed in javascript, so you should keep track of what is currently pressed as well
var pressed = {
up: false,
down: false,
left: false,
right: false
};
var LEFT_ARROW = 37;
var UP_ARROW = 38;
var RIGHT_ARROW = 39;
var DOWN_ARROW = 40;
document.onkeydown = function (e) {
e = e || window.event;
switch (e.keyCode) {
case LEFT_ARROW:
pressed.left = true;
break;
case UP_ARROW:
pressed.up = true;
break;
case RIGHT_ARROW:
pressed.right = true;
break;
case DOWN_ARROW:
pressed.down = true;
break;
default:
break;
}
}
//update position separately
function updatePos() {
if (pressed.up) { //change y up }
if (pressed.down) { //change y down }
if (pressed.left) { //change x left }
if (pressed.right) { //change x right }
}
Hope this helps, and good luck!