1

I'm working on a snake game project (For a JavaScript course). I try to avoid a "bug" that when two keys are used simultaneously, the game considers that there is collision while visually no.

Snake Game Error

Code for keyboard key management

document.onkeydown = function handleKeyDown(e){
    const key = e.keyCode;
    let newDirection;
    switch(key){
        case 37:
            newDirection = "left";
            break;
        case 38:
            newDirection = "up";
            break;
        case 39:
            newDirection = "right";
            break;
        case 40:
            newDirection = "down";
            break;
        case 32:
            restart();
            return;
        default:
            return;
    }
    snakee.setDirection(newDirection);
}

How to avoid this? Someone told me about the preventDefault () event, should I use it?

EDIT1

Here is the checkCollision method that is part of my constructor function for the snake:

this.checkCollision = function(){
            let wallCollision = false;
            let snakeCollision = false;
            const head = this.body[0];
            const rest = this.body.slice(1);
            const snakeX = head[0];
            const snakeY = head[1];
            const minX = 0;
            const minY = 0;
            const maxX = widthInBlocks-1;
            const maxY = heightInBlocks-1;
            const isNotBetweenHorizontalWalls = snakeX < minX || snakeX > maxX;
            const isNotBetweenVerticalWalls = snakeY < minY || snakeY > maxY;
            if(isNotBetweenHorizontalWalls || isNotBetweenVerticalWalls){
                wallCollision = true;
            }
            for(let i = 0;i < rest.length;i++){
                if(snakeX == rest[i][0] && snakeY == rest[i][1]){
                    snakeCollision = true;
                }
            }
            return wallCollision || snakeCollision;
        };

EDIT2:

So, after your answers, here is the solution used.

I used a switch before, I decided to use an if/else instead.

const map = {};
  onkeydown = onkeyup = function(e){
    e = e || event;
    map[e.keyCode] = e.type == 'keydown';
    let newDirection;
    if(map[37]){
      newDirection = "left";
    } else if(map[38]){
      newDirection = "up";
    } else if(map[39]){
      newDirection = "right";
    } else if(map[40]){
      newDirection = "down";
    } else if(map[32]){
      restart();
    }
    snakee.setDirection(newDirection);
  }

BUT, the problem still exist if all the properties of the map object are at false, what happens when I press two keys simultaneously.

Object {
  32: false,
  37: false,
  38: false,
  39: false,
  40: false
}

I don't know how to prevent this.

Quentin Rey
  • 91
  • 1
  • 9
  • What is your collision detection code? – Mark Baijens Jan 21 '19 at 09:01
  • You need to add more code. You can, for example, add a flag `snakeMoved` and check it on `keydown` event, so that you don't move the snake another way while the first key didn't yet finish it's move. – Roberto Zvjerković Jan 21 '19 at 09:02
  • 2
    Similar question is asked here: How to detect if multiple keys are pressed at once using JavaScript? https://stackoverflow.com/questions/5203407/how-to-detect-if-multiple-keys-are-pressed-at-once-using-javascript – Prashant Zombade Jan 21 '19 at 09:04

4 Answers4

1

what do you mean by collision? The onkeydown event will only return the last key pressed on the keyboard even if multiple keys are held down.

If you press down then left, the onkeydown event will return the keycode for left key.

You don't need to use event.preventDefault() as this will stop looking for a keydown key & instead return nothing.

Your code should work fine with what I see.

If you want to detect multiple keystrokes then you should keep a map object for that.

const keysPressed = {};
onkeydown = function(e) => {
  const e = e || event;
  keyPressed[e.keyCode] = e.keyCode >= 37 && e.keyCode <= 40;

  // move your snake now with the keys pressed
}
Danyal Imran
  • 2,515
  • 13
  • 21
1

This is a well known issue in game building, with the best way to do this being by queuing actions. This may be via flags, or an actual queue. On key-press the user actions is noted, or added to a list, and executed.

Your game loop is responsible to checks the flags, or the inputs queue, at the appropriate time and update the game state accordingly.

The following tutorial will give some insights into how to implement, and update basic input states: https://developer.mozilla.org/en-US/docs/Games/Tutorials/2D_Breakout_game_pure_JavaScript/Paddle_and_keyboard_controls

Daniel ZA
  • 306
  • 2
  • 10
1

Please take a look at this example on how to handle simultaneous keystrokes.

Click on the results screen with the console opened and press top + left or right and you'll see custom console.log to each one :)

var map = {}; // You could also use an array
onkeydown = onkeyup = function(e){
    e = e || event; // to deal with IE
    map[e.keyCode] = e.type == 'keydown';
    /* insert conditional here */
    console.log(map);
  
  if(map[38] && map[39]) {
    console.log('Moved Top & Right!')
  }
  
    if(map[38] && map[37]) {
    console.log('Moved Top & Left!')
  }
  

}

window.addEventListener('keydown', onkeyup);
window.addEventListener('keyup', onkeydown);

Example made based on this question: How to detect if multiple keys are pressed at once using JavaScript?

0

you should use onkeyup event in place of onkeydown it will solve your collision issue.

Ved_Code_it
  • 704
  • 6
  • 10