entire program pauses when loop is broken

Viewed 105

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!

4 Answers

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!

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

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