entire program pauses when loop is broken - javascript

I have this code which makes a square move, but when i for example press the right arrow key then the left arrow key and release the right arrow key, the square pauses for a while and then just moves the left.
is there a way to get rid of this pause?
this is the code i have now:
let velocity = 10
let x = 375
let y = 225
setInterval(function(){
document.getElementById("square").style.left = x + "px";
document.getElementById("square").style.top = y + "px";
}, 1)
var direction = ""
function currentDirection(movingToDirection){
if(movingToDirection != direction){
return true
}
else {
return false
}
}
function Sup() {
if(currentDirection("Sup")){
direction = "Sup";
var Sloopup = setInterval(function(){
y -= velocity/10
}, 1)
window.Sloopup = Sloopup
}
}
function Sdown() {
if(currentDirection("Sdown")){
direction = "Sdown";
var Sloopdown = setInterval(function(){
y += velocity/10
}, 1)
window.Sloopdown = Sloopdown
}
}
function Sleft() {
if(currentDirection("Sleft")){
direction = "Sleft";
var Sloopleft = setInterval(function(){
x -= velocity/10
}, 1)
window.Sloopleft = Sloopleft
}
}
function Sright() {
if(currentDirection("Sright")){
direction = "Sright";
var Sloopright = setInterval(function(){
x += velocity/10
}, 1)
window.Sloopright = Sloopright
}
}
function Break(Function) {
direction = ""
if (Function = "Sup") {
clearInterval(window.Sloopup)
} if (Function = "Sdown") {
clearInterval(window.Sloopdown)
} if (Function = "Sleft") {
clearInterval(window.Sloopleft)
} if (Function = "Sright") {
clearInterval(window.Sloopright)
}
}
document.addEventListener("keydown", event => {
if(event.key==="ArrowUp") {Sup()}
if(event.key==="ArrowDown") {Sdown()}
if(event.key==="ArrowLeft") {Sleft()}
if(event.key==="ArrowRight") {Sright()}
})
document.addEventListener("keyup", event => {
if(event.key==="ArrowUp") {Break("Sup")}
if(event.key==="ArrowDown") {Break("Sdown")}
if(event.key==="ArrowLeft") {Break("Sleft")}
if(event.key==="ArrowRight") {Break("Sright")}
})
and I also have a online example:
Online example
Any help is very appreciated!

Try this:
var Keys = {
up: false,
down: false,
left: false,
right: false
};
let y = 10;
let x = 10;
document.addEventListener("keydown", (event) => {
if (event.key === "ArrowLeft") Keys.left = true;
else if (event.key === "ArrowUp") Keys.up = true;
else if (event.key === "ArrowRight") Keys.right = true;
else if (event.key === "ArrowDown") Keys.down = true;
});
document.addEventListener("keyup", (event) => {
if (event.key === "ArrowLeft") Keys.left = false;
else if (event.key === "ArrowUp") Keys.up = false;
else if (event.key === "ArrowRight") Keys.right = false;
else if (event.key === "ArrowDown") Keys.down = false;
});
setInterval(() => {
if (Keys.up) {
y -= 1;
} else if (Keys.down) {
y += 1;
}
if (Keys.left) {
x -= 1;
} else if (Keys.right) {
x += 1;
}
}, 1);
setInterval(() => {
document.getElementById("app").style.left = x + "px";
document.getElementById("app").style.top = y + "px";
}, 1);
There might be some delay when you clear and set a new interval. Here I set 2 interval that keep listening to keydown, which you will need to clear later when the game ends
Here's the sandbox

so below my suggestion if I understand well your problem:
document.addEventListener("keydown", event => {
// the issue occurs here, you have to disable all current keydown event
Break("Sup");Break("Sdown");Break("Sleft");Break("Sright");
if(event.key==="ArrowUp") {Sup()}
if(event.key==="ArrowDown") {Sdown()}
if(event.key==="ArrowLeft") {Sleft()}
if(event.key==="ArrowRight") {Sright()}
})
BTW i tested it & it works, i hope that it helps you.
Good luck!

as well as we separate content from style same concept applies in different layers of the engine you need to separate the logic from the engine as well. The idea is separation so you can implement/debug much easier. Example code below:
const velocity = {x:0, y:0}
const position = {x:375, y:225}
const keys = { ArrowLeft:false, ArrowRight:false, ArrowUp:false, ArrowDown:false }
function UpdatePlayerInput()
{
velocity.x = ((+keys.ArrowRight) - (+keys.ArrowLeft)) * 10;
velocity.y = ((+keys.ArrowDown) - (+keys.ArrowUp)) * 10;
}
function PyhsicsUpdate(){
position.x += velocity.x / 10;
position.y += velocity.y / 10;
}
function RenderUpdate() {
if (document.getElementById("square").style.left !== position.x + "px")
document.getElementById("square").style.left = position.x + "px";
if (document.getElementById("square").style.top !== position.y + "px")
document.getElementById("square").style.top = position.y + "px";
}
setInterval(PyhsicsUpdate, 1)
setInterval(RenderUpdate, 10)
document.addEventListener("keydown", event => {
keys[event.key] = true;
UpdatePlayerInput();
})
document.addEventListener("keyup", event => {
keys[event.key] = false;
UpdatePlayerInput();
})

Javascript is a heavily event-driven, often asynchronous language.
window.setTimeout(() => console.log(1), 0);
console.log(2);
The above will log 2, then 1, despite the timeout having been set to 0 milliseconds. setTimeout and setTimeout delay execution of the passed function until the first tick of the event loop, after the specified interval / timeout having been exceeded. It is not expected to be immediately invoked, even with a zero-value time argument.
is there a way to get rid of this pause?
Several approaches exist.
Whichever you end up choosing, the key adjustments you have to make is to (a) separate application state from input state and (b) only update input state as a direct result from event handlers, derive position and velocity on the following (regularly scheduled) view state update. Have one data structure hold the current state of the key(s) pressed and another positional arguments.
Prior to writing code, formalize the expected outcome. What is supposed to happen, if for instance both left and right are pressed? No acceleration in either direction, because inputs cancel? Bail early and save yourself the superfluous zero-sum arithmetic.
First (last) pressed takes precedence? Don't use an object with boolean values to represent pressed keys, use a Set (or array if unavailable) and add / remove (push / prune) to it, encode order by time of insertion.
// wrap the pure string representations of keys in a common data structure
const Key = {
DOWN: 'ArrowDown',
LEFT: 'ArrowLeft',
RIGHT: 'ArrowRight',
UP: 'ArrowUp'
}
// holds currently pressed keys
const inputState = new Set([])
function handleKeyDown(event) {
inputState.add(event.key)
}
function handleKeyUp(event) {
inputState.delete(event.key)
}
document.addEventListener('keydown', handleKeyDown)
document.addEventListener('keyup', handleKeyUp)
const viewState = {
velocity: {
x: 10,
y: 10
},
position: {
x: 375,
y: 225
}
}
const targetDomNode = document.getElementById('square')
function updateTargetDomNode(x, y) {
targetDomNode.style.left = `${x}px`
targetDomNode.style.top = `${y}px`
}
function renderViewState() {
updateTargetDomNode(
viewState.position.x,
viewState.position.y
)
}
// you could abstract more, obviously
function accelerate() {
viewState.velocity.x += inputState.has(Key.LEFT) ? -1 : 0
viewState.velocity.x += inputState.has(Key.RIGHT) ? 1 : 0
viewState.velocity.y += inputState.has(Key.UP) ? -1 : 0
viewState.velocity.y += inputState.has(Key.DOWN) ? 1 : 0
}
function updatePosition(timeSinceLastUpdate) {
viewState.position.x += viewState.velocity.x * timeSinceLastUpdate
viewState.position.y += viewState.velocity.y * timeSinceLastUpdate
}
const UPDATE_INTERVAL = 50
setInterval(() => {
accelerate()
// note, this will be slightly more than 50ms,
// if you care for accuracy, use a measurement,
// rather than a static value
updatePosition(UPDATE_INTERVAL)
renderViewState()
}, UPDATE_INTERVAL)

Related

Detect multiple keypresses and trigger an action once in p5.js

I want the user to be able to press a+d once and my game should increase the score, not hold it to increase score, similar to when pressing a+d causes a special effect to occur in a game.
This code did not work so I didn't bother to call it fail attempts test.
function keyPressed() {
if (key == 'a' && key == 'd')
score += 1;
}
This is my first failing attempt:
function keyPressed() {
if (key == 'a' || key == 'd')
score += 1;
}
This is my second failing attempt.
The reason for it is because when I press 1 button, it still increases the score and when pressing 2 buttons and holding them to increase faster but does not stop, which is not what I have in mind.
Score2keypress.js:
let RightButtom = false;
let LeftButtom = false;
let character = {
"score": 0
}
function setup() {
createCanvas(600, 600);
}
function draw() {
background(220);
// draw score character
fill(0, 0, 255); //move6
text("Score: " + character.score, 20, 120);
// update score increase character
if (RightButtom) {
character.score += 1;
} //move8
if (LeftButtom) {
character.score += 1;
} //move10
/////////////ScoreExtra
// show boolean values onscreen for clarity
textSize(20);
text("RightButtom = " + RightButtom +
"\nLeftButtom = " + LeftButtom, 10, 10, width / 2, height / 2);
}
//////////////ScoreExtra
function keyPressed() {
if (key == 'a') {
LeftButtom = true;
}
if (key == 'd') {
RightButtom = true;
}
}
function keyReleased() {
if (key == 'a') {
LeftButtom = false;
}
if (key == 'd') {
RightButtom = false;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>
There are a few approaches. The event-driven approach is using p5's keyPressed and keyReleased callbacks, pulling the key's code string from the native KeyboardEvent object. Adding and removing these key codes to a pressed set lets you implement variants of single-trigger logic.
const character = {score: 0};
const pressed = new Set();
function setup() {
createCanvas(600, 600);
textSize(20);
}
function draw() {
background(220);
text("Score: " + character.score, 20, 50);
}
function keyPressed(evt) {
const {code} = evt;
if (!pressed.has(code)) {
pressed.add(code);
if (pressed.has("KeyA") && pressed.has("KeyD")) {
character.score++;
}
}
}
function keyReleased(evt) {
pressed.delete(evt.code);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
If you want to detect and take action purely in draw, p5.js provides keyIsDown which accepts a parameter keycode.
const character = {score: 0};
const pressed = new Set();
function setup() {
createCanvas(600, 600);
textSize(20);
}
function draw() {
background(220);
text("Score: " + character.score, 20, 50);
if (keyIsDown(65) && keyIsDown(68)) {
if ((keyIsDown(65) && !pressed.has(68)) ||
(!pressed.has(65) && keyIsDown(68))) {
character.score++;
}
pressed.add(65);
pressed.add(68);
}
if (!keyIsDown(65)) {
pressed.delete(65);
}
if (!keyIsDown(68)) {
pressed.delete(68);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
As mentioned, there are multiple behavioral variants you can implement. Here's a variant that prevents retriggers with the same key:
const character = {score: 0};
const pressed = new Set();
function setup() {
createCanvas(600, 600);
textSize(20);
}
function draw() {
background(220);
text("Score: " + character.score, 20, 50);
if (keyIsDown(65) && !pressed.has(65) && // 'a' and 'd'
keyIsDown(68) && !pressed.has(68)) {
character.score++;
pressed.add(65);
pressed.add(68);
}
if (!keyIsDown(65)) {
pressed.delete(65);
}
if (!keyIsDown(68)) {
pressed.delete(68);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
In all above cases, the behavior lets the user press and hold one key indefinitely, then press the other. If you want these keys to be pressed on the exact same frame, or within n frames, you could remove the key from the set after it's been in there for n frames and add it to a requireRetrigger set that will prevent it from firing an action until it's been released. keyReleased might be handy here.
To detect to keys pressed together you need to monitor their state using keydown and keyup events.
var obj_down = {}
window.addEventListener("keydown", function(ev) {
if (obj_down[event.key]) {
// prevent multiple triggering
return
}
obj_down[event.key] = true;
if (obj_down["a"] && obj_down["d"]) {
console.log("a and d were pressed together! score++");
}
})
window.addEventListener("keyup", function(ev) {
delete obj_down[event.key];
})
Focus me with mouse then you can press "a" and "d" together

Move HTML object with JavaScript while key is pressed

I'm writing a little game with vueJS as the frontend. I managed to create all the needed objects and now I want to make them move if a key is pressed.
My problem is I can only move them once when the key is pressed. I want to move it consistently while the key is pressed.
My Code for moving is:
let myCircle = document.querySelector('.ball');
var key_left = false;
window.addEventListener('load', () =>{
myCircle.style.position = 'absolute';
myCircle.style.left = 0 + 'px';
myCircle.style.top = 1200 + 'px';
});
window.addEventListener('keyup', (event) => {
if (event.key == 'ArrowLeft')
key_left = true;
if (key_left == true)
{
myCircle.style.left = parseInt(myCircle.style.left) - 5 + 'px';
}
When I tried to add a while loop it stopped moving at all.
Code:
let myCircle = document.querySelector('.ball');
var key_left = false;
window.addEventListener('load', () =>{
myCircle.style.position = 'absolute';
myCircle.style.left = 0 + 'px';
myCircle.style.top = 1200 + 'px';
});
window.addEventListener('keyup', (event) => {
if (event.key == 'ArrowLeft')
key_left = true;
while (key_left == true)
{
myCircle.style.left = parseInt(myCircle.style.left) - 5 + 'px';
}
});
Does someone have a hint for me?
Thanks!
Try to use the keydown event instead, it is fired every tick.
Or check out the examples at w3school.
https://www.w3schools.com/jsref/event_onkeydown.asp
window.addEventListener('keydown', (event) => {
myCircle.style.left = parseInt(myCircle.style.left) - 5 + 'px'
}

HTML Audio - The volume provided (-0.1) is outside the range - Math max not working

I have created a volume down button I am trying to prevent the volume from going under 0.0. For some reason, the Math.max is not working correctly. Not sure what I am doing wrong by checking the current value/time.
How can I add a logic that If the volume is equal to 0.0 to return and do nothing?
See -> Arrow down keyCode 40.
(() => {
const audio = document.querySelector('audio');
const playButton = document.getElementById('play');
const progress = document.querySelector('.player__progress');
const progressBar = document.querySelector('.player__progress__inner');
const next = document.getElementById('next');
const prev = document.getElementById('previous');
const title = document.querySelector('.title');
const status = document.querySelector('.status');
const poster = document.querySelector('.player__image');
if (!audio) {
return;
}
// On play button click.
const onPlayStart = () => {
audio.paused ? audio.play() : audio.pause();
};
// On audio playing.
const onPlaying = () => {
playButton.innerHTML = `<i class="fas fa-pause"></i>`;
// Change title if is currently playing.
// status.innerText = `status: Wordt afgespeeld.`;
};
// On audio paused.
const onPaused = () => {
playButton.innerHTML = `<i class="fas fa-play"></i>`;
// Change title if is currently paused.
// status.innerText = `status: Is gepauzeerd.`;
};
// Update progress bar
const onTimeUpdate = () => {
const percent = (audio.currentTime / audio.duration) * 100;
progressBar.style.width = `${percent}%`;
};
// Update progress on click
const onProgressUpdate = (e) => {
audio.currentTime = (e.offsetX / progress.clientWidth) * audio.duration;
};
const onKeyPress = (e) => {
e.preventDefault();
// Spacebar
if (e.keyCode === 32) {
audio.paused ? audio.play() : audio.pause();
}
// Right arrow
if (e.keyCode === 39) {
// Forward by 10 sec.
audio.currentTime += 10;
}
// Left arrow
if (e.keyCode === 37) {
// Backward by 10 sec
audio.currentTime -= 10;
}
// Up arrow
if (e.keyCode === 38) {
// Volume up
if (audio.volume < 1.0) {
audio.volume += 0.1;
}
}
// Arrow down
if (e.keyCode === 40) {
// Volume down
if (audio.volume === Math.max(0, audio.volume - 0.01)) {
return;
}
if (audio.volume >= 0.0) {
audio.volume -= 0.1;
}
}
};
audio.addEventListener('play', onPlaying);
audio.addEventListener('pause', onPaused);
audio.addEventListener('timeupdate', onTimeUpdate);
document.addEventListener('keydown', onKeyPress);
progress.addEventListener('click', onProgressUpdate);
playButton.addEventListener('click', onPlayStart);
})();
You could use these functions below. They check if a value has reached a certain threshold and make a calculation if they don't, otherwise it just returns the value.
This means, in case for the increment, that it keeps adding until 1 is reached and then just returns 1 without counting upwards, effectively stopping the increment.
The calculation is because of floating point numbers not being precise. This makes sure that every value is exactly a one decimal point number (0.1, 0.2, 0.3, ect).
const incrementVolume = value =>
value < 1 ? (value * 10 + 1) / 10 : value;
const decrementVolume = value =>
value > 0 ? (value * 10 - 1) / 10 : value;
Then implement these functions in your if statements where they reassign the volume property.
// Arrow up
if (e.keyCode === 38) {
// Volume up
audio.volume = incrementVolume(audio.volume);
}
// Arrow down
if (e.keyCode === 40) {
// Volume down
audio.volume = decrementVolume(audio.volume);
}

How can I make box transition only for horizontal and vertical?

I'm trying to make a box moving by horizontal and vertical on the arrows press. But when I pres for example up arrow and right arrow it goes by diagonal.
Here is my codepen
.box {
background-color: gray;
height: 100px;
width: 100px;
transition: margin 0.5s cubic-bezier(0, .7, 0, 1);
}
const box = document.getElementsByClassName('box')[0];
document.addEventListener('keydown', function({keyCode, which}) {
const keycode = keyCode ? keyCode : which,
startValue = '0px',
shiftValue = '400px';
console.log(box.style.marginLeft, box.style.marginTop)
switch(keycode) {
case(40):
box.style.marginTop = shiftValue;
break;
case(39):
box.style.marginLeft = shiftValue;
break;
case(38):
box.style.marginTop = startValue;
break;
case(37):
box.style.marginLeft = startValue;
break;
}
});
Try the following:
const box = document.getElementsByClassName('box')[0];
let moving = false;
document.addEventListener('keydown', function({keyCode, which}) {
const keycode = keyCode ? keyCode : which,
startValue = '0px',
shiftValue = '400px';
console.log(box.style.marginLeft, box.style.marginTop)
if (!moving && [37, 38, 39, 40].includes(keycode)){
switch(keycode) {
case(40):
box.style.marginTop = shiftValue;
break;
case(39):
box.style.marginLeft = shiftValue;
break;
case(38):
box.style.marginTop = startValue;
break;
case(37):
box.style.marginLeft = startValue;
break;
}
moving = true;
window.setTimeout(() => moving = false, 400); // Not 500, because visually it is very slow towards the end anyway.
}
});
Either you could implement a Promise solution in here but I don't think it's worth justifying. Basically what we can do is to store all the keys pressed in an array and make sure the javascript only goes through the array of pressed keys once every x milliseconds.
const box = document.getElementsByClassName('box')[0];
let pressedKeys = [];
let timeoutHandler = -1;
document.addEventListener('keydown', function({keyCode, which}) {
// As soon as the user presses a key we will clear the time out
clearTimeout(timeoutHandler);
const keycode = keyCode ? keyCode : which,
startValue = '0px',
shiftValue = '400px';
pressedKeys.push(keycode);
// register the timeout to a variable in order for this function
// only run once every x second. This implementation is also
// known as 'debounce function to poor people'
timeoutHandler = setTimeout(() => {
pressedKeys.forEach((key, index) => {
// The animation time for each key pressed will be incremental
// which means the second key pressed will have an animation delay
// higher than the first one
const timeoutSeconds = index === 0 ? 1 : index + (index * 100);
setTimeout(() => {
switch(key) {
case(40):
box.style.marginTop = shiftValue;
break;
case(39):
box.style.marginLeft = shiftValue;
break;
case(38):
box.style.marginTop = startValue;
break;
case(37):
box.style.marginLeft = startValue;
break;
}
}, timeoutSeconds)
});
pressedKeys = [];
}, 100)
});
One possible solution: you could keep track of which moves have been requested and wait to perform the move until the previous move has finished. Example:
const box = document.getElementsByClassName('box')[0];
const startValue = '0px';
const shiftValue = '400px';
function moveDown() {
// The move* functions only perform the move if is valid - i.e.,
// if it would actually cause a visible change.
if (box.style.marginTop !== shiftValue) {
box.style.marginTop = shiftValue;
return true;
}
// The move* functions return true iff the move was made.
return false;
}
function moveRight() {
if (box.style.marginLeft !== shiftValue) {
box.style.marginLeft = shiftValue;
return true;
}
return false;
}
function moveUp() {
if (box.style.marginTop !== startValue) {
box.style.marginTop = startValue;
return true;
}
return false;
}
function moveLeft() {
if (box.style.marginLeft !== startValue) {
box.style.marginLeft = startValue;
return true;
}
return false;
}
const moves = [];
let timeOfLastMoveInMilliseconds = null;
const animationDurationInSeconds = 0.5; // should match css transition duration
const animationDurationInMilliseconds = animationDurationInSeconds * 1000;
function onFrame() {
if (!timeOfLastMoveInMilliseconds) {
timeOfLastMoveInMilliseconds = performance.now();
}
const timeSinceLastMove = performance.now() - timeOfLastMoveInMilliseconds;
if (moves.length > 0 && timeSinceLastMove >= animationDurationInMilliseconds) {
const wasMoved = moves.pop()();
if (wasMoved) {
timeOfLastMoveInMilliseconds = performance.now();
}
}
window.requestAnimationFrame(onFrame);
}
window.requestAnimationFrame(onFrame);
document.addEventListener('keydown', function({keyCode, which}) {
const keycode = keyCode ? keyCode : which;
switch(keycode) {
case(40):
moves.unshift(moveDown);
break;
case(39):
moves.unshift(moveRight);
break;
case(38):
moves.unshift(moveUp);
break;
case(37):
moves.unshift(moveLeft);
break;
}
});
Note that the code above keeps track of every move the user makes, so, e.g., if you press down, up, down, up, down quickly, that sequence will play back. Depending on your application, you might want to add restrictions so that, e.g., only horizontal+diagonal moves are allowed and/or only moves coming from keypresses that happen within a short timeframe.
So, I decided to set 500 milliseconds breaks between every press on the key with animation and if it's faster without animation, but without any additional asynchronous code. Thanks for the help.
.box {
background-color: gray;
height: 100px;
width: 100px;
}
.animation {
transition: margin 0.5s cubic-bezier(0, .7, 0, 1);
}
const box = document.getElementsByClassName('box')[0];
let prevTime = 0;
document.addEventListener('keydown', function({keyCode, which}) {
const keycode = keyCode ? keyCode : which,
startValue = '0px',
shiftValue = '400px',
currentTime = Date.now();
let diff = 0;
diff = currentTime - prevTime;
if (diff > 500) {
box.classList.add('animation');
} else {
box.classList.remove('animation');
}
switch(keycode) {
case(40):
box.style.marginTop = shiftValue;
break;
case(39):
box.style.marginLeft = shiftValue;
break;
case(38):
box.style.marginTop = startValue;
break;
case(37):
box.style.marginLeft = startValue;
break;
}
prevTime = currentTime;
});

How to change sprite's direction to diagonal if two movement keys are pressed simultaneously

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);

Categories