Search code examples
javascriptcanvasonkeydownonkeyup

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

Solution

  • 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