Now that we have our main game objects in place, we need to hand some control over to the user. In this chapter well do this using keyboard events. In JavaScript, you can use many events to control your program. We use events by defining an event handler (a function) that will be called with the event object as an argument when the event is triggered. In this case, well be using keyboard eventsin particular, the keyDown and keyUp events. These are called when keys are pressed and released. The event object contains information about which key triggered the event.
Controlling Pac-Man
Lets demonstrate how to use keyboard events with a simple example. Take a copy of your exercise8 folder and save it as exercise10. Well upgrade the example to take user input. First, we need to add a few methods to our PacMan model. Add the new methods shown in Listing to your PacMan object.
PacMan.prototype.move_right = function () {
this .x_speed = this .speed;
this .y_speed = 0;
this .angle = 0;
}
PacMan.prototype.move_down = function () {
this .x_speed = 0;
this .y_speed = this .speed;
this .angle = 0.5 * Math.PI;
}
PacMan.prototype.move_left = function () {
this .x_speed = - this .speed;
this .y_speed = 0;
this .angle = Math.PI;
}
PacMan.prototype.move_up = function () {
this .x_speed = 0;
this .y_speed = - this .speed;
this .angle = 1.5 * Math.PI;
}
Listing 10-1
Add Controls to PacMan
Now run the code in your browser and try typing pacman.move_up() in the console. Youll see that you have some control over Pac-Mans movements. Now we need to hook up an event handler for the keyDown event. Add the code in Listing to the end of your HTML script.
window.onkeydown = function (e) {
let key = e.key || e.keyCode;
switch (key) {
case "ArrowLeft":
case 37: // left arrow keyCode
pacman.move_left();
break ;
case "ArrowUp":
case 38: // up arrow keyCode
pacman.move_up();
break ;
case "ArrowRight":
case 39: // right arrow keyCode
pacman.move_right();
break ;
case "ArrowDown":
case 40: // down arrow keyCode
pacman.move_down();
break ;
}
}
Listing 10-2
Handle the onkeydown Event
Now refresh the browser, and you should be able to control Pac-Man by pressing the arrow keys! The handler is triggered on every key press with an event object as the argument. The event attribute event.key contains a string representation for the key that was pressed. In some browsers (including Safari at the time of writing), the event.key attribute isnt supported, so we fall back to a deprecated API using event.keyCode . This is irritating but necessary if you want to support the most common browsers. For a list of keyCodes, see http://keycode.info .
Note that the original behavior of occasionally changing direction is still included in the PacMan.prototype.update method. Remove it to give yourself full control over Pac-Man. You can remove quite a lot of code.
This approach has a serious problem. The arrow keys already have a function in most browsers. Imagine your canvas is part of a larger page with content below it. Its normal and expected that the arrow keys can be used to scroll the page down to see that content. But because we placed the event handler on the main window object, were overriding this behavior, and now the arrow keys wont scroll the page. A more polite approach is to add the handler just to the canvas element. This requires the focus to be set to the canvas element in order for the key presses to be handled by our code. If the canvas doesnt have the focus, the handler isnt triggered .
First, we need to give our canvas a tabindex attribute. Without this, you cant set the focus on the canvas. Edit yourelement, as shown in Listing .
Listing 10-3
Handle the onkeydown Event
Now, we could simply change the reference from window.onkeydown to context.canvas.onkeydown . Alternatively, we can define our handler as a function, as shown in Listing .
function keydown_handler(e) {
let key = e.key || e.keyCode;
let nothing_handled = false ;
switch (key) {
case "ArrowLeft":
case 37: // left arrow keyCode
pacman.move_left();
break ;
case "ArrowUp":
case 38: // up arrow keyCode
pacman.move_up();
break ;
case "ArrowRight":
case 39: // right arrow keyCode
pacman.move_right();
break ;
case "ArrowDown":
case 40: // down arrow keyCode
pacman.move_down();
break ;
default :
nothing_handled = true ;
}
if (!nothing_handled) e.preventDefault();
}
Listing 10-4
An Event Handler Function
Notice that weve ensured that were overriding only the keys we want to use. Were using the default case to set the nothing_handled variable when no keys are being handled. Notice also the additional call to e.preventDefault() only applies when something has been handled by our code. This call stops the default behavior of the event (for example, scrolling the window) from happening. This means that standard key combinations (such as Ctrl+R to refresh the page) arent blocked. We only block default behavior for our chosen keys.
Now we can connect it in a more general way with the addEventListener method. This will only apply when a key is pressed and the canvas has the focus. Listing connects the keydown event to our handler function.
context.canvas.addEventListener("keydown", keydown_handler);
context.canvas.focus();
Listing 10-5
Add an Event Listener and Set the Focus on the Canvas
We also set the focus on the canvas programmatically so theres no need to click the canvas. We can unset the focus by clicking away from the canvas with the mouse or by using Tab to cycle around all the selectable elements in the page. To get a visual indication of whether the canvas has the focus, update your styles.css file as shown in Listing .
body {
text-align : center ;
font-family : sans-serif ;
}
canvas {
background-color : black ;
border : 10 px solid white ;
}
canvas :focus {
border : 10 px solid grey ;
}
Listing 10-6
Highlight the Canvas on Focus
Now if we click anywhere else on the page, our canvas will lose focus, and normal key handling will resume. If we then click the canvas, we get control of the game, and the page no longer scrolls. We should also be able to refresh the page with Ctrl+R while the canvas has the focus. Nice.