Tutorial: How to make a top-down shooter in JavaScript

Lesson 6) Detecting multiple key presses at once

Annie are you OK?

So we've made our four-cornered amigo move, rook-like, around our game area. A good start, but the movement is very jerky. As noted previously, this is because the event listener is only passing the most recently detected key press to our playerMove function.

To make the movement smooth, we need to change our approach slightly.

First a reminder - the below object is what the event listener passes to the playerMove function:
keydown { target: , key: "w", charCode: 0, keyCode: 87 })
This is for the "w" key. Note that there's a "key" property, but also a "keyCode." In JavaScript, every key has a special code number associated with it. For our movement keys, these codes are:

w = 87
a = 65
s = 83
d = 68

Now hold that thought for a second. Hopefully you're at least somewhat familiar with arrays. An array is a bit like a filing cabinet with a load of drawers. These "drawers" can hold variables, objects, or even other arrays. Each drawer in the array is given an index number, which you use to tell your program to go and get whatever is inside that drawer. Let's define an array called keys, like this:
var keys = [];
Now let's update our event listeners as follows:
canvas.addEventListener("keydown", function (e) {
keys[e.keyCode] = true;
});

canvas.addEventListener("keyup", function (e) {
keys[e.keyCode] = false;
});
If you refer to an array and put a number in square brackets after it, you are referring to the "drawer" with that index number. For example, "console.log(keys[87]);" will display on the console whatever is in position 87 in the array called "keys".

Our event listeners are now going into the keys array, and going to position e.keyCode and setting it to "true" if the user presses that key, and "false" if the user takes their finger off the key. For example, if you press "w" the code works like this:
  1. When you press "w", the event listener passes an object to a function. The function labels the object "e".
  2. The "e" object has a property called keyCode, to which JavaScript automatically assigns the unique code for the key that was pressed.
  3. The "w" key's keyCode is 87. Therefore e.keyCode = 87.
  4. "keys[e.keyCode] = true;" is therefore the same as "keys[87] = true". It gives the variable at position 87 in the keys array the value "true".
  5. When the key is released, the keyup event listener changes keys[87] to "false".
Now we can detect multiple key presses. We just need to check if the values at positions 65, 68, 83 and 87 in the keys array are set to true.

We can update playerMove to do just that:
function playerMove(e){

if (keys[87]) {
Player1.y -= 1;
}
if (keys[83] ) {
Player1.y += 1;
}
if (keys[65] ) {
Player1.x -= 1;
}
if (keys[68] ) {
Player1.x += 1;
}
return false;
}
Saying "if (keys[87])" is equivalent to saying "if (keys[87] === true)". So now playerMove checks the keys array, and checks whether the variables in posisions 87, 83, 65 and 68 are set to "true". These correspond to the w, s, a and d keys, of course. If any of these are true, playerMove updates the player's x or y coordinates accordingly. If not, the program does nothing.

We just need to do one more thing. Previously we called playerMove whenever we detected a key press. Now, we're updating the values in the keys[] array whenever we detect a keypress. So there's no longer a line of code telling JavaScript to run the playerMove function. No problem, just add the following into mainDraw:
playerMove();
In every iteration of the game loop, our program will run playerMove, and check whether any keys are currently being pressed.


From rook to queen

Now our champion can move vertically, horizontally, and diagonally! Sweet! We can change directions smoothly, too.

But, as the old saying goes, the devil makes work for idle right-angles. He's going to get pretty bored just walking around that empty green field. Let's give him something to do.

Go to Lesson 7 - Collision detection