Adding key listeners

Dealing with key events

Reminders from the HTML5 part 1 course

This has been something of a nightmare for years, as different browsers had different ways of handling key events and key codes (read this if you are fond of JavaScript archaeology). Fortunately, it’s much improved today and we can rely on methods that should work in any browser less than four years old.

After a keyboard-related event (eg keydown or keyup), the code of the key that fired the event will be passed to the listener function.  It is possible to test which key has been pressed or released, like this:

  1. window.addEventListener(‘keydown’, function(event) {
  2.    if (event.keyCode === 37) {
  3.      // Left arrow was pressed
  4.    }
  5. }, false);

At line 2, the key code of 37 corresponds to the left arrow key.

You can try key codes with this interactive example, and here is a list of keyCodes (from CSS Tricks):

JavaScript key codes

Game requirements: managing multiple keypress / keyrelease events

In a game, we often need to check which keys are being used, at a very high frequency – typically from inside the game loop that is looping at up to 60 times per second.

If a spaceship is moving left, chances are you are keeping the left arrow down, and if it’s firing missiles at the same time you must also be pressing the space bar like a maniac, and maybe pressing the shift key to release smart bombs.

Sometimes these three keys might be down at the same time, and the game loop will have to take these three keys into account: move the ship left, release a new missile if the previous one is out of the screen or if it reached a target, launch a smart bomb if conditions are met, etc.

Keep the list of pertinent keys in a javascript object

The typical method used is: store the list of the keys (or mouse button or whatever game pad button…) that are up or down at a given time in a JavaScript object. For our small game engine we will call this object “inputStates“.

We will update its content inside the different input event listeners, and later check its values inside the game loop to make the game react accordingly.

Add this to our game framework:

So, these are the changes to our small game engine prototype (which is far from finished yet):

    1. We add an empty inputStates object as a global property of the game engine,
    2. In the start() method, we add event listeners for each keydown and keyup event which controls the game.
    3. In each listener, we test if an arrow key or the space bar has been pressed or released, and we set the properties of the inputStates object accordingly. For example, if the space bar is pressed, we set inputStates.space=true; but if it’s released, we reset to inputStates.space=false.
    4. In the main loop (to prove everything is working), we add tests to check which keys are down; and if a key is down, we print its name on the canvas.

Here is the online example you can try at JSBin

trembling monster with multiple key press management.

And here is the complete source code:

  1. // Inits
  2. window.onload = function init() {
  3.   var game = new GF();
  4.   game.start();
  5. };
  6. // GAME FRAMEWORK STARTS HERE
  7. var GF = function(){
  8.    … 
  9.    // vars for handling inputs
  10.    var inputStates = {};
  11.    var measureFPS = function(newTime){
  12.       
  13.    };
  14.    // Clears the canvas content
  15.    function clearCanvas() {
  16.      ctx.clearRect(0, 0, w, h);
  17.    }
  18.    // Functions for drawing the monster and perhaps other objects
  19.    function drawMyMonster(x, y) {
  20.       
  21.    }
  22.    var mainLoop = function(time){
  23.      // Main function, called each frame
  24.      measureFPS(time);
  25.      // Clears the canvas
  26.      clearCanvas();
  27.      // Draws the monster
  28.      drawMyMonster(10+Math.random()*10, 10+Math.random()*10);
  29.     // check inputStates
  30.     if (inputStates.left) {
  31.       ctx.fillText(“left”, 150, 20);
  32.     }
  33.     if (inputStates.up) {
  34.       ctx.fillText(“up”, 150, 50);
  35.     }
  36.    if (inputStates.right) {
  37.       ctx.fillText(“right”, 150, 80);
  38.    }
  39.    if (inputStates.down) {
  40.      ctx.fillText(“down”, 150, 120);
  41.    }
  42.    if (inputStates.space) {
  43.      ctx.fillText(“space bar”, 140, 150);
  44.    }
  45.    // Calls the animation loop every 1/60th of second
  46.    requestAnimationFrame(mainLoop);
  47. };
  48. var start = function(){
  49.     
  50.     // Important, we will draw with this object
  51.     ctx = canvas.getContext(‘2d’);
  52.     // Default police for text
  53.     ctx.font=“20px Arial”;
  54.     // Add the listener to the main, window object, and update the states
  55.     window.addEventListener(‘keydown’, function(event){
  56.       if (event.keyCode === 37) {
  57.         inputStates.left = true;
  58.       } else if (event.keyCode === 38) {
  59.         inputStates.up = true;
  60.       } else if (event.keyCode === 39) {
  61.         inputStates.right = true;
  62.       } else if (event.keyCode === 40) {
  63.         inputStates.down = true;
  64.       } else if (event.keyCode === 32) {
  65.         inputStates.space = true;
  66.       }
  67.     }, false);
  68.     // If the key is released, change the states object
  69.     window.addEventListener(‘keyup’, function(event){
  70.       if (event.keyCode === 37) {
  71.         inputStates.left = false;
  72.       } else if (event.keyCode === 38) {
  73.         inputStates.up = false;
  74.       } else if (event.keyCode === 39) {
  75.         inputStates.right = false;
  76.       } else if (event.keyCode === 40) {
  77.         inputStates.down = false;
  78.       } else if (event.keyCode === 32) {
  79.         inputStates.space = false;
  80.       }
  81.     }, false);
  82.    // Starts the animation
  83.    requestAnimationFrame(mainLoop);
  84.  };
  85. // our GameFramework returns a public API visible from outside its scope
  86. return {
  87.    start: start
  88. };
  89. };

You may notice that on some computers / operating systems, it is not possible to simultaneously press the up and down arrow keys, or left and right arrow keys, because they are mutually exclusive. However space + up + right should work in combination.

Leave a comment