Javascript Game Foundations - Player Input
Thu, Dec 5, 2013Ten Essential Foundations of Javascript Game Development
- A Web Server and a Module Strategy
- Loading Assets
- The Game Loop
- Player Input
- Math
- DOM
- Rendering
- Sound
- State Management
- Juiciness
Player Input
Games are interactive. Without input from a player, they would be stories.
Input via the keyboard and mouse is the most common method for games that run on desktop or laptop computers, ← ↑ → ↓, W,A,S,D,<ENTER>,<SPACE>, mouse-look, etc.
Input via a touch screen is added for games that run on mobile devices.
Event Handlers
In order to get input from the player we need to attach event handlers to the browser DOM. This was
more complex back when we had to support older browsers, but modern browsers support the
standard addEventListener
method:
Most games run standalone within a single web page, so we typically want to attach our keyboard
event handlers to the top-level document
object in order to handle all keyboard events within
the page, independent of focus.
For mouse and touch events, it usually makes more sense to anchor those handlers to the HTML5 <canvas> element to make calculating relative coordinates simpler.
document.addEventListener('keydown', onkeydown, false);
document.addEventListener('keyup', onkeyup, false);
canvas.addEventListener('click', onclick, false);
canvas.addEventListener('mousemove' onmousemove, false);
canvas.addEventListener('touchstart', ontouchstart, false);
canvas.addEventListener('touchmove', ontouchmove, false);
function onkeydown(event) {
...
}
function onkeyup(event) {
...
}
function onclick(event) {
...
}
function onmousemove(event) {
...
}
function ontouchstart(event) {
...
}
function ontouchmove(event) {
...
}
If the event was handled by our game, we should also prevent the browser’s default action (e.g. to stop the up/down keys from scrolling the page)
function onkeydown(event) {
...
event.preventDefault();
}
Keyboard Input
Once we have attached to the keydown
and keyup
events, we can use the event.keyCode
attribute to check which key has been pressed.
If we are already using a 3rd party library such as jQuery, they may provide an enumeration of the
possible keyCode
values for us, but if not, we can easily define our own.
Here are some common values:
var KEY = {
BACKSPACE: 8,
TAB: 9,
RETURN: 13,
ESC: 27,
SPACE: 32,
PAGEUP: 33,
PAGEDOWN: 34,
END: 35,
HOME: 36,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
INSERT: 45,
DELETE: 46,
ZERO: 48, ONE: 49, TWO: 50, THREE: 51, FOUR: 52, FIVE: 53, SIX: 54, SEVEN: 55, EIGHT: 56, NINE: 57,
A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, Y: 89, Z: 90,
TILDA: 192
};
Most of the time, the keyboard event handlers should simply record the current state of the key,
pressed vs not pressed. It should still be the responsibility of the update()
method in
the game loop to actually update the game world.
Since we only need to record the state (key pressed vs not pressed) we can use a common onkey
function for both keydown
and keyup
events and record the state in a player.input
object:
var player = {
...
input: { left: false, right: false, jump: false }
}
document.addEventListener('keydown', function(ev) { return onkey(ev, ev.keyCode, true); }, false);
document.addEventListener('keyup', function(ev) { return onkey(ev, ev.keyCode, false); }, false);
function onkey(ev, key, pressed) {
switch(key) {
case KEY.LEFT: player.input.left = pressed; ev.preventDefault(); break;
case KEY.RIGHT: player.input.right = pressed; ev.preventDefault(); break;
case KEY.SPACE: player.input.jump = pressed; ev.preventDefault(); break;
}
}
Now our update()
method can modify the game state based on player.input
.
NOTE: I haven’t used it yet, but have recently discovered a small micro-library called keypress that looks like a promising, lightweight library for dealing with more complex keyboard handling scenarios (such as modifier-keys, combo-keys, etc) - check it out!
Touch Input
To detect a touch enabled device, you can use the Modernizr library, or you can treat Modernizr more like a mentor and just learn from it:
var hasTouch = ('ontouchstart' in window);
The touch events follow a similar pattern to mouse events:
- touchstart
- touchmove
- touchend
canvas.addEventListener('touchmove', ontouchmove, false);
Touch events include the targetTouches
attribute which is a list of fingers touching the current
DOM element. For the simple case, you can assume a single finger touch:
function ontouchmove(ev) {
var x = ev.targetTouches[0].pageX;
...
}
Others have written about touch events in much more detail.
- HTML5 Rocks - Touch Events
- Mozilla - Touch Events
- Quirksmode - Touch Events
- W3C - Touch Events
- Can I Use - Touch Events
An important note about the touch events and coordinates. Supposedly, they should provide 3
sets of coordinates: client, page and screen. It makes sense in a <canvas>
game to respond to
the touch events on the <canvas>
element and use the client coordinates to know where on the
canvas was touched.
This works great in iOS, but on some android devices the clientX and clientY coordinates are incorrect, they actually contain the same values as the pageX and pageY coordinates.
Ugh! This means you can’t rely on clientX and clientY, so you have to use pageX/Y instead and convert it to canvas relative coordinates yourself - be wary.
Mouse Input
I haven’t actually had the need for mouse input in any of my games so far, so I’m going to cheat a little here…
… blah blah blah mousedown
, mouseup
, mouseover
, mouseout
, and mousemove
, blah blah blah
blah blah click
and dblclick
blah blah blah…
… and simply refer you to others who have written more extensively about this subject than I:
Hmmm, I bet I could post articles a lot more frequently if I could just write ‘blah blah blah’ :-)