Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Revision 1109267 of Desktop mouse and keyboard controls

  • Revision slug: Games/Techniques/Control_mechanisms/Desktop_with_mouse_and_keyboard
  • Revision title: Implementing input controls for desktop with mouse and keyboard
  • Revision id: 1109267
  • Created:
  • Creator: chrisdavidmills
  • Is current revision? No
  • Comment Games/Techniques/Input_controls/Desktop_with_mouse_and_keyboard Games/Techniques/Control_mechanisms/Desktop_with_mouse_and_keyboard

Revision Content

Now when we have our mobile controls in place and the game playable on touch-enabled devices, it would be good to add mouse and keyboard support, so the game can be playable also on desktop. That way we can broaden the list of supported platforms the game can be played on.

It's also easier to test the controls-independent features like gameplay on desktop if you develop it there, so you don't have to push the files to a mobile device everytime you make a change in the source code.

The game Captain Rogers is built with Phaser and managing the controls is Phaser-based, but it could also be done in pure JavaScript. The good thing about using Phaser is that it offers helper variables and functions for easier and faster development, but it's totally up to you which approach you'll chose.

Pure JavaScript approach

Let's try to implement the pure JavaScript controls in the game first and see how it will work. First, let's set up the event listener to listen for the pressed keys:

document.addEventListener('keydown', keyDownHandler, false);

Whenever any key is down, we're executing the keyDownHandler function. Inside of it we can get the code if the key that was pressed and compare it with whatever we currently need. There are no helpers so you have to remember what the given codes are. For example 37 is the left arrow:

function keyDownHandler(event) {
    if(event.keyCode === '37') {
        // move left
    }
    // else if ...
}

If the code of the key that was just pressed is 37, then we can perform the action assigned to it - move the ship left. If there will be more keys to check we can do it in a switch statement:

document.onkeydown = function(event) {
    var code = event.keyCode;
    switch(code) {
        case 37: {
            // move left
            break;
        }
        case 38: {
            // move up
            break;
        }
        case 39: {
            // move right
            break;
        }
        case 40: {
            // move down
            break;
        }
    }
};

We can write our own KeyCode object containing the key codes, for example:

var KeyboardHelper = { left: 37, up: 38, right: 39, down: 40 };

That way instead of using the codes to compare the input we could do something like this:

if(event.keyCode === KeyboardHelper.left) {
    // move left
}

And it will work. We can assign any actions in the given cases and situations, with or without Phaser.

Phaser approach

As I mentioned before you can write everything on your own, but you can also take the advantage of built-in Phaser functions that will make your life easier and development a lot faster. All the edge cases, differences between browser implementations of various features are covered by the framework, so you can focus on the actual task you want to do.

Mouse

The mouse interactions in the game are focused on clicking the buttons. In Phaser the buttons you create will take any type of input, whether it's a touch on mobile or click on desktop. That way, if you already implemented the buttons the mobile controls section way it will work out of the box on the desktop too.

var buttonEnclave = this.add.button(10, 10, 'logo-enclave', this.clickEnclave, this);

The button will be placed ten pixels from the top left edge of the screen, will use the logo-enclave image, and will execute clickEnclave function when clicked. We can assign actions directly to the buttons:

this.buttonShoot = this.add.button(this.world.width*0.5, 0, 'button-alpha', null, this);
this.buttonShoot.onInputDown.add(this.shootingPressed, this);
this.buttonShoot.onInputUp.add(this.shootingReleased, this);

The button used for shooting works perfectly fine for both mobile and desktop approach.

If you want to use the mouse's cursor position on the screen you can do so with this.game.input.mousePointer. Let's assume you'd like to shoot a bullet when the right half of the screen is clicked with a mouse - it would be something like this:

if(this.game.input.mousePointer.isDown) {
    if(this.game.input.mousePointer.x > this.world.width*0.5) {
        // shoot
    }
}

If you'd like to differentiate the mouse buttons being pressed, there are three defaults you can pick from:

this.game.input.mousePointer.leftButton.isDown;
this.game.input.mousePointer.moddleButton.isDown;
this.game.input.mousePointer.rightButton.isDown;

Keep in mind that instead of mousePointer, it's better to use activePointer for platform independent input if you want to keep the support for mobile interactions.

Keyboard

The whole game can be controlled with just the keyboard and nothing else. The built in this.game.input.keyboard object manages the input from the keyboard and have a few helpful methods like addKey or isDown. There's also the Phaser.KeyCode object containing all the available keyboard keys:

---IMG-KEYS---

In the main menu of the game we can add an extra way to begin playing. The Start button can be clicked to do so, but we can use the Enter key to do the same:

var keyEnter = this.game.input.keyboard.addKey(Phaser.KeyCode.ENTER);
var keyEnter.onDown.add(this.clickStart, this);

You can use addKey to add any key Phaser.KeyCode object can offer. The onDown function is executed whenever the Enter key is pressed - it will launch the clickStart method which starts a new game.

It's only an extra way to do it, but it's an option to play the game on desktop without using a mouse, so you don't have to take your hands off the keyboard.

Controlling the game

We can start supporting the keyboard input in games built with Phaser by creating the basic cursor keys in the create function:

this.cursors = this.input.keyboard.createCursorKeys();

It creates four directional arrow keys for us:

this.cursors.left;
this.cursors.right;
this.cursors.up;
this.cursors.down;

You can also define the keys on your own and offer an alternative, WASD control mechanism:

this.keyLeft = this.input.keyboard.addKey(Phaser.KeyCode.A);
this.keyRight = this.input.keyboard.addKey(Phaser.KeyCode.D);
this.keyUp = this.input.keyboard.addKey(Phaser.KeyCode.W);
this.keyDown = this.input.keyboard.addKey(Phaser.KeyCode.S);

Now we have both cursor and WASD keys depending on the player preferences:

if(this.cursors.left.isDown || this.keyLeft.isDown) {
    // move left
}
else if(this.cursors.right.isDown || this.keyRight.isDown) {
    // move right
}
if(this.cursors.up.isDown || this.keyUp.isDown) {
    // move up
}
else if(this.cursors.down.isDown || this.keyDown.isDown) {
    // move down
}

In the update function we can now move the player's ship in any direction using one of the two movement keys setup.

To accomodate that we can also offer the usual and the alternative firing controls. For cursor keys the natural shooting button would be on the other side of the keyboard, so the player can use the other hand - for example the X key, for WASD keys it can be the Space:

this.keyFire1 = this.input.keyboard.addKey(Phaser.KeyCode.X);
this.keyFire2 = this.input.keyboard.addKey(Phaser.KeyCode.SPACEBAR);

Then, in the update function we can check if any of those two is pressed:

if(this.keyFire1.isDown || this.keyFire2.isDown) {
    // fire the weapon
}

If yes, then it's time to shoot some bullets! We can even define a secret cheat button:

this.keyCheat = this.input.keyboard.addKey(Phaser.KeyCode.C);

And then in the update function whenever it is pressed:

if(this.keyCheat.isDown) {
    this.player.health = this.player.maxHealth;
}

We can set the health of the player to maximum. Remember: it's a secret, so don't tell anyone!

How to play

We've implemented the controls, and now it's recommended to inform the player about his options to control the game - he wouldn't know about them otherwise. When showing how to play screen where the various ways to control the ship in the game are shown, instead of showing them all we can detect whether the game is launched on desktop or mobile:

if(this.game.device.desktop) {
    moveText = 'Arrow keys or WASD to move';
    shootText = 'X or Space to shoot';
}
else {
    moveText = 'Tap and hold to move';
    shootText = 'Tap to shoot';
}

If the game is running on desktop the cursor and WASD keys message will be shown - if not, then the message we have for mobile will be.

---IMG_HOW_TO_PLAY_SCREEN_DESKTOP---

To skip how to play without setting up a new key for it, we can listen for any key and move on:

this.input.keyboard.onDownCallback = function() {
    if(this.stateStatus == 'intro') {
        this.hideIntro();
    }
};

It hides the intro and starts the actual game.

Pause and game over screens

To make the game fully playable with keyboard it should be possible to go back to the main menu from pause and game over screens, continue playing or restarting the game. It can be done exactly the same as before - by capturing key codes and performing actions. For example, by pressing Phaser.KeyCode.Backspace or Phaser.KeyCode.Delete you can hook up the action related to the Back button in the game.

Summary

Now when the mouse and keyboard controls are in place on top of mobile controls it's time to spice things up and add Gamepad API support which will make the game even more enjoyable.

Revision Source

<p class="summary">Now when we have our mobile controls in place and the game playable on touch-enabled devices, it would be good to add mouse and keyboard support, so the game can be playable also on desktop. That way we can broaden the list of supported platforms the game can be played on.</p>

<p>It's also easier to test the controls-independent features like gameplay on desktop if you develop it there, so you don't have to push the files to a mobile device everytime you make a change in the source code.</p>

<p class="note">The game Captain Rogers is built with Phaser and managing the controls is Phaser-based, but it could also be done in pure JavaScript. The good thing about using Phaser is that it offers helper variables and functions for easier and faster development, but it's totally up to you which approach you'll chose.</p>

<h2>Pure JavaScript approach</h2>

<p>Let's try to implement the pure JavaScript controls in the game first and see how it will work. First, let's set up the event listener to listen for the pressed keys:</p>

<pre>
document.addEventListener('keydown', keyDownHandler, false);</pre>

<p>Whenever any key is down, we're executing the <code>keyDownHandler</code> function. Inside of it we can get the code if the key that was pressed and compare it with whatever we currently need. There are no helpers so you have to remember what the given codes are. For example <code>37</code> is the left arrow:</p>

<pre>
function keyDownHandler(event) {
    if(event.keyCode === '37') {
        // move left
    }
    // else if ...
}</pre>

<p>If the code of the key that was just pressed is <code>37</code>, then we can perform the action assigned to it - move the ship left. If there will be more keys to check we can do it in a switch statement:</p>

<pre>
document.onkeydown = function(event) {
    var code = event.keyCode;
    switch(code) {
        case 37: {
            // move left
            break;
        }
        case 38: {
            // move up
            break;
        }
        case 39: {
            // move right
            break;
        }
        case 40: {
            // move down
            break;
        }
    }
};</pre>

<p>We can write our own <code>KeyCode</code> object containing the key codes, for example:</p>

<pre>
var KeyboardHelper = { left: 37, up: 38, right: 39, down: 40 };</pre>

<p>That way instead of using the codes to compare the input we could do something like this:</p>

<pre>
if(event.keyCode === KeyboardHelper.left) {
    // move left
}</pre>

<p>And it will work. We can assign any actions in the given cases and situations, with or without Phaser.</p>

<h2>Phaser approach</h2>

<p>As I mentioned before you can write everything on your own, but you can also take the advantage of built-in Phaser functions that will make your life easier and development a lot faster. All the edge cases, differences between browser implementations of various features are covered by the framework, so you can focus on the actual task you want to do.</p>

<h3>Mouse</h3>

<p>The mouse interactions in the game are focused on clicking the buttons. In Phaser the buttons you create will take any type of input, whether it's a touch on mobile or click on desktop. That way, if you already implemented the buttons the mobile controls section way it will work out of the box on the desktop too.</p>

<pre>
var buttonEnclave = this.add.button(10, 10, 'logo-enclave', this.clickEnclave, this);</pre>

<p>The button will be placed ten pixels from the top left edge of the screen, will use the <code>logo-enclave</code> image, and will execute <code>clickEnclave</code> function when clicked. We can assign actions directly to the buttons:</p>

<pre>
this.buttonShoot = this.add.button(this.world.width*0.5, 0, 'button-alpha', null, this);
this.buttonShoot.onInputDown.add(this.shootingPressed, this);
this.buttonShoot.onInputUp.add(this.shootingReleased, this);</pre>

<p>The button used for shooting works perfectly fine for both mobile and desktop approach.</p>

<p>If you want to use the mouse's cursor position on the screen you can do so with <code>this.game.input.mousePointer</code>. Let's assume you'd like to shoot a bullet when the right half of the screen is clicked with a mouse - it would be something like this:</p>

<pre>
if(this.game.input.mousePointer.isDown) {
    if(this.game.input.mousePointer.x &gt; this.world.width*0.5) {
        // shoot
    }
}</pre>

<p>If you'd like to differentiate the mouse buttons being pressed, there are three defaults you can pick from:</p>

<pre>
this.game.input.mousePointer.leftButton.isDown;
this.game.input.mousePointer.moddleButton.isDown;
this.game.input.mousePointer.rightButton.isDown;</pre>

<p>Keep in mind that instead of <code>mousePointer</code>, it's better to use <code>activePointer</code> for platform independent input if you want to keep the support for mobile interactions.</p>

<h3>Keyboard</h3>

<p>The whole game can be controlled with just the keyboard and nothing else. The built in <code>this.game.input.keyboard</code> object manages the input from the keyboard and have <a href="https://phaser.io/docs/2.6.1/Phaser.Keyboard.html#methods">a few helpful methods</a> like <code>addKey</code> or <code>isDown</code>. There's also the <a href="https://phaser.io/docs/2.6.1/Phaser.KeyCode.html#members">Phaser.KeyCode</a> object containing all the available keyboard keys:</p>

<p>---IMG-KEYS---</p>

<p>In the main menu of the game we can add an extra way to begin playing. The Start button can be clicked to do so, but we can use the <code>Enter</code> key to do the same:</p>

<pre>
var keyEnter = this.game.input.keyboard.addKey(Phaser.KeyCode.ENTER);
var keyEnter.onDown.add(this.clickStart, this);</pre>

<p>You can use <code>addKey</code> to add any key <code>Phaser.KeyCode</code> object can offer. The <code>onDown</code> function is executed whenever the <code>Enter</code> key is pressed - it will launch the <code>clickStart</code> method which starts a new game.</p>

<p>It's only an extra way to do it, but it's an option to play the game on desktop without using a mouse, so you don't have to take your hands off the keyboard.</p>

<h3>Controlling the game</h3>

<p>We can start supporting the keyboard input in games built with Phaser by creating the basic cursor keys in the <code>create</code> function:</p>

<pre>
this.cursors = this.input.keyboard.createCursorKeys();</pre>

<p>It creates four directional arrow keys for us:</p>

<pre>
this.cursors.left;
this.cursors.right;
this.cursors.up;
this.cursors.down;</pre>

<p>You can also define the keys on your own and offer an alternative, <code>WASD</code> control mechanism:</p>

<pre>
this.keyLeft = this.input.keyboard.addKey(Phaser.KeyCode.A);
this.keyRight = this.input.keyboard.addKey(Phaser.KeyCode.D);
this.keyUp = this.input.keyboard.addKey(Phaser.KeyCode.W);
this.keyDown = this.input.keyboard.addKey(Phaser.KeyCode.S);</pre>

<p>Now we have both cursor and <code>WASD</code> keys depending on the player preferences:</p>

<pre>
if(this.cursors.left.isDown || this.keyLeft.isDown) {
    // move left
}
else if(this.cursors.right.isDown || this.keyRight.isDown) {
    // move right
}
if(this.cursors.up.isDown || this.keyUp.isDown) {
    // move up
}
else if(this.cursors.down.isDown || this.keyDown.isDown) {
    // move down
}</pre>

<p>In the <code>update</code> function we can now move the player's ship in any direction using one of the two movement keys setup.</p>

<p>To accomodate that we can also offer the usual and the alternative firing controls. For cursor keys the natural shooting button would be on the other side of the keyboard, so the player can use the other hand - for example the <code>X</code> key, for <code>WASD</code> keys it can be the <code>Space</code>:</p>

<pre>
this.keyFire1 = this.input.keyboard.addKey(Phaser.KeyCode.X);
this.keyFire2 = this.input.keyboard.addKey(Phaser.KeyCode.SPACEBAR);</pre>

<p>Then, in the update function we can check if any of those two is pressed:</p>

<pre>
if(this.keyFire1.isDown || this.keyFire2.isDown) {
    // fire the weapon
}</pre>

<p>If yes, then it's time to shoot some bullets! We can even define a secret cheat button:</p>

<pre>
this.keyCheat = this.input.keyboard.addKey(Phaser.KeyCode.C);</pre>

<p>And then in the update function whenever it is pressed:</p>

<pre>
if(this.keyCheat.isDown) {
    this.player.health = this.player.maxHealth;
}</pre>

<p>We can set the health of the player to maximum. Remember: it's a secret, so <em>don't tell anyone</em>!</p>

<h3>How to play</h3>

<p>We've implemented the controls, and now it's recommended to inform the player about his options to control the game - he wouldn't know about them otherwise. When showing how to play screen where the various ways to control the ship in the game are shown, instead of showing them all we can detect whether the game is launched on desktop or mobile:</p>

<pre>
if(this.game.device.desktop) {
    moveText = 'Arrow keys or WASD to move';
    shootText = 'X or Space to shoot';
}
else {
    moveText = 'Tap and hold to move';
    shootText = 'Tap to shoot';
}</pre>

<p>If the game is running on desktop the cursor and <code>WASD</code> keys message will be shown - if not, then the message we have for mobile will be.</p>

<p>---IMG_HOW_TO_PLAY_SCREEN_DESKTOP---</p>

<p>To skip how to play without setting up a new key for it, we can listen for any key and move on:</p>

<pre>
this.input.keyboard.onDownCallback = function() {
    if(this.stateStatus == 'intro') {
        this.hideIntro();
    }
};</pre>

<p>It hides the intro and starts the actual game.</p>

<h3>Pause and game over screens</h3>

<p>To make the game fully playable with keyboard it should be possible to go back to the main menu from pause and game over screens, continue playing or restarting the game. It can be done exactly the same as before - by capturing key codes and performing actions. For example, by pressing <code>Phaser.KeyCode.Backspace</code> or <code>Phaser.KeyCode.Delete</code> you can hook up the action related to the <code>Back</code> button in the game.</p>

<h2>Summary</h2>

<p>Now when the mouse and keyboard controls are in place on top of mobile controls it's time to spice things up and add Gamepad API support which will make the game even more enjoyable.</p>
Revert to this revision