Handling input in a javascript game
published 21 jul 2012
In my last post about writing html5/js games,
I focused on how to structure your game to keep the code manageable. I established a simple
structure consisting of a simple Game
object that manages the objects in the world and
the animation loop, and a Drawable
object that provides a simple interface for the Game
object to draw and interact with objects that exist.
In this post, I’m going to continue with the same game concept, this time adding input handling,
which will allow the player to move their little guy around. If you think about it, there are really two parts to being able to handle input. The first
part is keeping track of what the actual input is—in this case, what keyboard keys are pressed—
and the second is reacting to that input. For this game, I created an InputManager
object
to track the input, and then the Player
or Drawable
objects can react accordingly.
Handling input is actually really easy. All we need to do is add a listener on the window or canvas’ keydown
event
that stores the key being pressed, and then another listener on keyup
that removes that
key from the list of active keys. The InputManager
is below:
var InputManager = inherits(function() { }, {
// keycodes from jQuery UI:
// https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.core.js
// thanks guys!
'Keys': {
'BACKSPACE': 8,
'COMMA': 188,
'DELETE': 46,
'DOWN': 40,
'END': 35,
'ENTER': 13,
'ESCAPE': 27,
'HOME': 36,
'LEFT': 37,
'NUMPAD_ADD': 107,
'NUMPAD_DECIMAL': 110,
'NUMPAD_DIVIDE': 111,
'NUMPAD_ENTER': 108,
'NUMPAD_MULTIPLY': 106,
'NUMPAD_SUBTRACT': 109,
'PAGE_DOWN': 34,
'PAGE_UP': 33,
'PERIOD': 190,
'RIGHT': 39,
'SPACE': 32,
'TAB': 9,
'UP': 38
},
'_keysDown': {},
'constructor': function() {
window.addEventListener('keydown', _.bind(this._onkeydown, this));
window.addEventListener('keyup', _.bind(this._onkeyup, this));
},
'_onkeydown': function(ev) {
// ev is the event object, and it has the "which" member,
// that says which key is pressed.
this._keysDown[ev.which] = true;
},
'_onkeyup': function(ev) {
this._keysDown[ev.which] = false;
},
'keyDown': function(key) {
// simply returning this._keysDown[key] would give "undefined"
// instead of false if the key was never pressed. Adding two
// "!"s makes sure that it's always either true or false
return !!this._keysDown[key];
}
});
The Game
object creates an InputManager
object when it starts, and then holds on to
it. The Drawable
objects all receive the Game
object when their draw
function is called,
so if they need to handle input, they can just use the InputManager
.
Here is an example of a player Drawable
, which checks for the left or right arrow keys,
and then moves the player left or right accordingly.
var Player = inherits(Drawable, {
'doDraw': function(centroid, dT, game) {
if(game.inputManager.keyDown(game.inputManager.Keys.LEFT)) {
centroid.x -= 20;
}
if(game.inputManager.keyDown(game.inputManager.Keys.RIGHT)) {
centroid.x += 20;
}
if(centroid.x - 5 < 0) {
centroid.x = 20;
}
if(centroid.x + 5 >= game.width) {
centroid.x = game.width - 6;
}
this.constructor.__super__.doDraw.call(this, centroid, dT, game);
}
});
Now we have a nice object we can use to make our game interactive, and have properly separated
out functionality between utility objects—InputManager
—and the actual objects that are
part of the game—like Player
. Aside from being nice and easy to read, this means
that when we go on to write our second game, we’ll have built up a nice library of objects
we can use to speed up the development process.