Search code examples
javascriptcanvas2d-games

document keyup multitasking


Im creating a small game in javascript that requires multiple keys to be pressed at once for an avatars movement.

To recognize multiple key presses I'm using "Braden Best"s answer to the question JavaScript multiple keys pressed at once which works well except for the fact that the document doesn't seem to multitask keyup events. For example if I want to press the up arrow key followed by the left arrow key then release the left arrow key the avatar would stop completely.

Here is an example code: https://Jsfiddle.net/552gc9dh/1/

var c = document.getElementById("canv");
var canv = c.getContext("2d");
console.log("test");

var map = {};
var playerlist = [];

function player(width, height, x, y, color, speedx, speedy) {
    this.width = width;
    this.height = height;
    this.x = x;
    this.y = y;
    this.color = color;
    this.speedx = speedx;
    this.speedy = speedy;
    playerlist.push(this);
}
var player1 = new player(50, 50, 0, 0, "red", 2, 2);
console.log(playerlist[0]);

function gravity(playerY) {


}

function createplayerlistener(name, key1, key2, key3, key4) {
    onkeydown = onkeyup = function(e) {
        e = e || event;
        map[e.keyCode] = e.type == 'keydown';
        if (name.x + name.speedx < c.width - name.width) {
            if (map[key1]) {
                name.x += name.speedx;
            }
        }
        if (name.x + name.speedx > 0) {

            if (map[key2]) {
                name.x -= name.speedx;
            }
        }
        if (name.y + name.speedy < c.height - name.height) {
            if (map[key3]) {
                name.y += name.speedy;
            }
        }
        if (name.y + name.speedy > 0) {
            if (map[key4]) {
                name.y -= name.speedy;
            }
        }

    }
}
createplayerlistener(player1, 39, 37, 40, 38);


setInterval(function() {

    canv.clearRect(0, 0, c.width, c.height);
    for (var i = 0; i <= playerlist.length - 1; i++) {
        canv.fillStyle = playerlist[i].color; // iterates through players and draws them
        canv.fillRect(playerlist[i].x, playerlist[i].y, playerlist[i].width, playerlist[i].height);
    }
}, 10); 

Solution

  • This answer is more of a strategy...

    I would set variables for directions and update them on keyDown and keyUp

    Sudo Code

    const North = false;
    const West = false;
    const South = false;
    const East = false;
    
    keyUp = keyDown = (keyType) {
      switch(keyType) {
        case 'up':
          North = !North;
          break;
        case 'right':
          East = !East;
          break;
        case 'down':
          South = !South;
          break;
        case 'left':
          West = !West;
          break;
      };
    } 
    
    setInterval({
      if(North) //move up
    
      if(South) //move down
    
      if(East) //Move right
    
      if(West) //Move left
    
    }, 10);
    

    This should maintain the current movement until you receive a command to cancel it. It will also allow up and down or right and left to counter each other if a user presses both.

    Hopefully this helps!


    Addition

    Likely a more performant option would actually be to create a setInteval on keydown for a specific direction and remove it on keyUp, that way if the player is not interacting you are not running extra cycles ever 1/100 of a second

    directions = {
      up: null,
      right: null,
      down: null,
      left: null
    }
    
    const startInterval = (keyType) => {
      direction[keyType] = setInteval(()=> move(keyType);
    }
    const endInterval = (keyType) => {
      clearInterval( direction[keyType]);
      direction[keyType] = null;
    }
    const keyUp = keyDown = (keyType) {
      if(direction[keyType] === null) {
        startInteval(keyType);
      } else { 
        endInterval(keyType);
      }
    }