Bruno Perković
Bruno Perković

Reputation: 43

Move player using keyboard in javascript

I am making a web game (not with webGL, but with html elements) and I need to move character with WASD keys. I tried these things:

  1. Used event listeners keydown and keyup. Problem is that it is unresponsive and doesnt work really well when multiple keys are pressed simultaneously

  2. Used setInterval(20ms) and event listeners. On my stronger laptop everything works fine, but I feel like it is using insane amount of cpu power because my laptop starts sounding like a plane. On weaker laptop it wasn't working as well as first one, it was choppy and laggy

keyDict = {}
document.addEventListener('keydown', key => keyDict[key.code] = true);
document.addEventListener('keyup', key => keyDict[key.code] = false);

moveID = setInterval(move, 20)
function move()
{
   if(!finished)
   {
      newDirs = [0,0]
      //Left
      if(keyDict.ArrowLeft == true) 
      {
         newDirs[0] -= 1;
      }
      //Right
      if(keyDict.ArrowRight == true) 
      {
         newDirs[0] += 1;
      }
      //Up
      if(keyDict.ArrowUp == true) 
      {
         newDirs[1] -= 1;
      }
      //Down
      if(keyDict.ArrowDown == true) 
      {
         newDirs[1] += 1;
      }
      map.updateDir(newDirs); 
   }
}
  1. Used requestAnimationFrame and event listeners. On stronger laptop it looks like it utilizes 144 fps and its even more smoother, but sometimes it doesn't even respond to my controls. My laptop still sounds as it is working too hard
keyDict = {}
document.addEventListener('keydown', key => keyDict[key.code] = true);
document.addEventListener('keyup', key => keyDict[key.code] = false);

requestAnimationsFrame(move)
function move()
{
    same code...
    requestAnimationFrame(move)
}

I want to make it responsive and very smooth and I know there is way but don't know how. Example of this is mouse, your laptop doesn't get worked up from scrolling (and, for example, moving google maps with mouse is smooth and doesn't use cpu as much).

Upvotes: 2

Views: 908

Answers (1)

Roko C. Buljan
Roko C. Buljan

Reputation: 206565

Don't use the 20ms interval.
Move the player inside the requestAnimationFrame depending on which key Event.code is pressed and holds a truthy value inside of your keyDict object:

const keyDict = {};
const Player = {
  el: document.querySelector("#player"),
  x: 200,
  y: 100,
  speed: 2,
  move() {
    this.el.style.transform = `translate(${this.x}px, ${this.y}px)`;
  }
};

const updateKeyDict = (ev) => {
    const k = ev.code;
    if (/^Arrow\w+/.test(k)) { // If is arrow
      ev.preventDefault();
      keyDict[k] = ev.type === "keydown"; // set boolean true / false
    }
};

const update = () => {
  // Determine move distance to account diagonal move: 1/Math.sqrt(2) = ~0.707
  let dist =
    keyDict.ArrowUp   && (keyDict.ArrowLeft || keyDict.ArrowRight) ||
    keyDict.ArrowDown && (keyDict.ArrowLeft || keyDict.ArrowRight) ? 0.707 : 1;
    
  dist *= Player.speed;
  
  if (keyDict.ArrowLeft)  Player.x -= dist;
  if (keyDict.ArrowUp)    Player.y -= dist;
  if (keyDict.ArrowRight) Player.x += dist;
  if (keyDict.ArrowDown)  Player.y += dist;
  Player.move();
}

document.addEventListener('keydown', updateKeyDict);
document.addEventListener('keyup', updateKeyDict);

(function engine() {
  update();
  window.requestAnimationFrame(engine);
}());
#player {
  position: absolute;
  left: 0;
  top: 0;
  width: 20px;
  height: 20px;
  background: #000;
  border-radius: 50%;
}
Click here to focus, and use arrows
<div id="player"></div>

The above example uses Event.code for Arrows, which gives "ArrowLeft/Up/Right/Down" but you can change it accordingly to use "KeyW/A/S/D" instead.

"WASD" keys example

const keyDict = {};
const Player = {
  el: document.querySelector("#player"),
  x: 200,
  y: 100,
  speed: 2,
  move() {
    this.el.style.transform = `translate(${this.x}px, ${this.y}px)`;
  }
};

const updateKeyDict = (ev) => {
  const k = ev.code;
  if (/^Key[WASD]/.test(k)) { // If is "KeyW,A,S,D" key
    ev.preventDefault();
    keyDict[k] = ev.type === "keydown"; // set boolean true / false
  }
};

const update = () => {
  // Determine move distance to account diagonal move: 1/Math.sqrt(2) = ~0.707
  let dist =
    keyDict.KeyW && (keyDict.KeyA || keyDict.KeyD) ||
    keyDict.KeyS && (keyDict.KeyA || keyDict.KeyD) ? 0.707 : 1;

  dist *= Player.speed;

  if (keyDict.KeyA) Player.x -= dist;
  if (keyDict.KeyW) Player.y -= dist;
  if (keyDict.KeyD) Player.x += dist;
  if (keyDict.KeyS) Player.y += dist;
  Player.move();
}

document.addEventListener('keydown', updateKeyDict);
document.addEventListener('keyup', updateKeyDict);

(function engine() {
  update();
  window.requestAnimationFrame(engine);
}());
#player {
  position: absolute;
  left: 0;
  top: 0;
  width: 20px;
  height: 20px;
  background: #000;
  border-radius: 50%;
}
Click here to focus, and use keys WASD
<div id="player"></div>

Upvotes: 3

Related Questions