Desktop gamepad controls
========================

* Previous
* Overview: Control mechanisms
* Next

Now we'll look at adding something extra — support for gamepad controls, via the Gamepad API. It brings a console-like experience to your web games.

The Gamepad API gives you the ability to connect a gamepad to your computer and detect pressed buttons directly from the JavaScript code thanks to the browsers implementing such feature. An API exposes all the information you need to hook up your game's logic and successfully control the user interface and gameplay.

API status, browser and hardware support
----------------------------------------

The Gamepad API is still in Working Draft status, although browser support is already quite good — around 63% global coverage, according to caniuse.com. The list of supported devices is also quite extensive — most popular gamepads (e.g. XBox 360 or PS3) should be suitable for web implementations.

Pure JavaScript approach
------------------------

Let's think about implementing pure JavaScript gamepad controls in our little controls demo first to see how it would work. First, we need an event listener to listen for the connection of the new device:


```js
window.addEventListener("gamepadconnected", gamepadHandler);
```


It's executed once, so we can create some variables we will need later on for storing the controller info and the pressed buttons:


```js
let controller = {};
let buttonsPressed = [];
function gamepadHandler(e) {
  controller = e.gamepad;
  output.textContent = `Gamepad: ${controller.id}`;
}
```


The second line in the `gamepadHandler` function shows up on the screen when the device is connected:

![Gamepad connected message under the Captain Rogers game - wireless XBox 360 controller.](/en-US/docs/Games/Techniques/Control_mechanisms/Desktop_with_gamepad/controls-gamepadtext.png)

We can also show the `id` of the device — in the case above we're using the XBox 360 wireless controller.

To update the state of the gamepad's currently pressed buttons we will need a function that will do exactly that on every frame:


```js
function gamepadUpdateHandler() {
  buttonsPressed = [];
  if (controller.buttons) {
    for (let b = 0; b < controller.buttons.length; b++) {
      if (controller.buttons[b].pressed) {
        buttonsPressed.push(b);
      }
    }
  }
}
```


We first reset the `buttonsPressed` array to get it ready to store the latest info we'll write to it from the current frame. Then, if the buttons are available we loop through them; if the `pressed` property is set to `true`, then we add them to the `buttonsPressed` array for later processing. Next, we'll consider the `gamepadButtonPressedHandler()` function:


```js
function gamepadButtonPressedHandler(button) {
  let press = false;
  for (let i = 0; i < buttonsPressed.length; i++) {
    if (buttonsPressed[i] === button) {
      press = true;
    }
  }
  return press;
}
```


The function takes a button as a parameter; in the loop it checks if the given button's number is among the currently pressed buttons available in the `buttonsPressed` array. If it is, then the function returns `true`; `false` otherwise.

Next, in the `draw()` function we do two things — execute the `gamepadUpdateHandler()` function to get the current state of pressed buttons on every frame, and use the `gamepadButtonPressedHandler()` function to check the buttons we are interested to see whether they are pressed, and do something if they are:


```js
function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // …

  gamepadUpdateHandler();
  if (gamepadButtonPressedHandler(0)) {
    playerY -= 5;
  } else if (gamepadButtonPressedHandler(1)) {
    playerY += 5;
  }
  if (gamepadButtonPressedHandler(2)) {
    playerX -= 5;
  } else if (gamepadButtonPressedHandler(3)) {
    playerX += 5;
  }
  if (gamepadButtonPressedHandler(11)) {
    alert("BOOM!");
  }

  // …

  ctx.drawImage(img, playerX, playerY);
  requestAnimationFrame(draw);
}
```


In this case, we are checking the four D-Pad buttons (0-3) and the A button (11).

**Note:** Please remember that different devices may have different key mappings, i.e. the D-Pad Right button have an index of 3 on the wireless XBox 360, but may have a different one on another device.

You could also create a helper function that would assign proper names to the listed buttons, so for example instead of checking out if `gamepadButtonPressedHandler(3)` is pressed, you could do a more descriptive check: `gamepadButtonPressedHandler('DPad-Right')`.

You can see a live demo in action — try connecting your gamepad and pressing the buttons.

Phaser approach
---------------

Let's move on to the final Gamepad API implementation in the Captain Rogers: Battle at Andromeda game we created with Phaser. This is pure JavaScript code however too, so can be used in any other project no matter what framework was used.

First off, we'll create a small library that will take care of handling the input for us. Here's the `GamepadAPI` object, which contains useful variables and functions:


```js
const GamepadAPI = {
  active: false,
  controller: {},
  connect(event) {},
  disconnect(event) {},
  update() {},
  buttons: {
    layout: [],
    cache: [],
    status: [],
    pressed(button, state) {},
  },
  axes: {
    status: [],
  },
};
```


The `controller` variable stores the information about the connected gamepad, and there's an `active` boolean variable we can use to know if the controller is connected or not. The `connect()` and `disconnect()` functions are bound to the following events:


```js
window.addEventListener("gamepadconnected", GamepadAPI.connect);
window.addEventListener("gamepaddisconnected", GamepadAPI.disconnect);
```


They are fired when the gamepad is connected and disconnected respectively. The next function is `update()`, which updates the information about the pressed buttons and axes.

The `buttons` variable contains the `layout` of a given controller (for example which buttons are where, because an XBox 360 layout may be different to a generic one), the `cache` containing the information about the buttons from the previous frame and the `status` containing the information from the current frame.

The `pressed()` function gets the input data and sets the information about it in our object, and the `axes` property stores the array containing the values signifying the amount an axis is pressed in the `x` and `y` directions, represented by a float in the `(-1, 1)` range.

After the gamepad is connected, the information about the controller is stored in the object:


```js
connect(event) {
    GamepadAPI.controller = event.gamepad;
    GamepadAPI.active = true;
},
```


The `disconnect` function removes the information from the object:


```js
disconnect(event) {
    delete GamepadAPI.controller;
    GamepadAPI.active = false;
},
```


The `update()` function is executed in the update loop of the game on every frame, so it contains the latest information on the pressed buttons:


```js
update() {
  GamepadAPI.buttons.cache = [];
  for (let k = 0; k < GamepadAPI.buttons.status.length; k++) {
    GamepadAPI.buttons.cache[k] = GamepadAPI.buttons.status[k];
  }
  GamepadAPI.buttons.status = [];
  const c = GamepadAPI.controller || {};
  const pressed = [];
  if (c.buttons) {
    for (let b = 0; b < c.buttons.length; b++) {
      if (c.buttons[b].pressed) {
        pressed.push(GamepadAPI.buttons.layout[b]);
      }
    }
  }
  const axes = [];
  if (c.axes) {
    for (let a = 0; a < c.axes.length; a++) {
      axes.push(c.axes[a].toFixed(2));
    }
  }
  GamepadAPI.axes.status = axes;
  GamepadAPI.buttons.status = pressed;
  return pressed;
},
```


The function above clears the buttons cache, and copies their status from the previous frame to the cache. Next, the button status is cleared and the new information is added. The same goes for the axes' information — looping through axes adds the values to the array. Received values are assigned to the proper objects and returns the pressed info for debugging purposes.

The `button.pressed()` function detects the actual button presses:


```js
pressed(button, hold) {
  let newPress = false;
  for (let i = 0; i < GamepadAPI.buttons.status.length; i++) {
    if (GamepadAPI.buttons.status[i] === button) {
      newPress = true;
      if (!hold) {
        for (let j = 0; j < GamepadAPI.buttons.cache.length; j++) {
          if (GamepadAPI.buttons.cache[j] === button) {
            newPress = false;
          }
        }
      }
    }
  }
  return newPress;
},
```


It loops through pressed buttons and if the button we're looking for is pressed, then the corresponding boolean variable is set to `true`. If we want to check the button is not held already (so it's a new press), then looping through the cached states from the previous frame does the job — if the button was already pressed, then we ignore the new press and set it to `false`.

Implementation
--------------

We now know what the `GamepadAPI` object looks like and what variables and functions it contains, so let's learn how all this is actually used in the game. To indicate that the gamepad controller is active we can show the user some custom text on the game's main menu screen.

The `textGamepad` object holds the text saying a gamepad has been connected, and is hidden by default. Here's the code we've prepared in the `create()` function that is executed once when the new state is created:


```js
create() {
  // …
  const message = 'Gamepad connected! Press Y for controls';
  const textGamepad = this.add.text(0, 0, message);
  textGamepad.visible = false;
}
```


In the `update()` function, which is executed every frame, we can wait until the controller is actually connected, so the proper text can be shown. Then we can keep the track of the information about pressed buttons by using the `Gamepad.update()` method, and react to the given information:


```js
update() {
  // …
  if (GamepadAPI.active) {
    this.textGamepad.visible = true;

    GamepadAPI.update();
    if (GamepadAPI.buttons.pressed('Start')) {
      // start the game
    }
    if (GamepadAPI.buttons.pressed('X')) {
      // turn on/off the sounds
    }

    this.screenGamepadHelp.visible = GamepadAPI.buttons.pressed('Y', 'hold');
  }
}
```


When pressing the `Start` button the relevant function will be called to begin the game, and the same approach is used for turning the audio on and off. There's an option wired up to show `screenGamepadHelp`, which holds an image with all the button controls explained — if the `Y` button is pressed and held, the help becomes visible; when it is released the help disappears.

![Gamepad info with all the available keys described and explained.](/en-US/docs/Games/Techniques/Control_mechanisms/Desktop_with_gamepad/controls-gamepadinfo.png)

On-screen instructions
----------------------

When the game is started, some introductory text is shown that shows you available controls — we are already detecting if the game is launched on desktop or mobile then showing a relevant message for the device, but we can go even further, to allow for the presence of a gamepad:


```js
create() {
  // …
  if (this.game.device.desktop) {
    if (GamepadAPI.active) {
      moveText = 'DPad or left Stick\nto move';
      shootText = 'A to shoot,\nY for controls';
    } else {
      moveText = 'Arrow keys\nor WASD to move';
      shootText = 'X or Space\nto shoot';
    }
  } else {
    moveText = 'Tap and hold to move';
    shootText = 'Tap to shoot';
  }
}
```


When on desktop, we can check if the controller is active and show the gamepad controls — if not, then the keyboard controls will be shown.

Gameplay controls
-----------------

We can offer even more flexibility to the player by giving him main and alternative gamepad movement controls:


```js
if (GamepadAPI.buttons.pressed("DPad-Up", "hold")) {
  // move player up
} else if (GamepadAPI.buttons.pressed("DPad-Down", "hold")) {
  // move player down
}

if (GamepadAPI.buttons.pressed("DPad-Left", "hold")) {
  // move player left
}

if (GamepadAPI.buttons.pressed("DPad-Right", "hold")) {
  // move player right
}

if (GamepadAPI.axes.status) {
  if (GamepadAPI.axes.status[0] > 0.5) {
    // move player up
  } else if (GamepadAPI.axes.status[0] < -0.5) {
    // move player down
  }

  if (GamepadAPI.axes.status[1] > 0.5) {
    // move player left
  } else if (GamepadAPI.axes.status[1] < -0.5) {
    // move player right
  }
}
```


They can now move the ship on the screen by using the `DPad` buttons, or the left stick axes.

Have you noticed that the current value of the axes is evaluated against `0.5`? It's because axes are having floating point values while buttons are booleans. After a certain threshold is reached we can assume the input is done deliberately by the user and can act accordingly.

For the shooting controls, we used the `A` button — when it is held down, a new bullet is spawned, and everything else is handled by the game:


```js
if (GamepadAPI.buttons.pressed("A", "hold")) {
  this.spawnBullet();
}
```


Showing the screen with all the controls looks exactly the same as in the main menu:


```js
this.screenGamepadHelp.visible = GamepadAPI.buttons.pressed("Y", "hold");
```


If the `B` button is pressed, the game is paused:


```js
if (gamepadAPI.buttonPressed("B")) {
  this.managePause();
}
```

The paused and gameover states
------------------------------

We already learned how to control the whole lifecycle of the game: pausing the gameplay, restarting it, or getting back to the main menu. It works smooth on mobile and desktop, and adding gamepad controls is just as straightforward — in the `update()` function, we check to see if the current state status is `paused` — if so, the relevant actions are enabled:


```js
if (GamepadAPI.buttons.pressed("Start")) {
  this.managePause();
}

if (GamepadAPI.buttons.pressed("Back")) {
  this.stateBack();
}
```


Similarly, when the `gameover` state status is active, then we can allow the user to restart the game instead of continuing it:


```js
if (GamepadAPI.buttons.pressed("Start")) {
  this.stateRestart();
}
if (GamepadAPI.buttons.pressed("Back")) {
  this.stateBack();
}
```


When the game over screen is visible, the `Start` button restarts the game while the `Back` button helps us get back to the main menu. The same goes for when the game is paused: the `Start` button unpauses the game and the `Back` button goes back, just like before.

Summary
-------

That's it! We have successfully implemented gamepad controls in our game — try connecting any popular controller like the XBox 360 one and see for yourself how fun it is to avoid the asteroids and shoot the aliens with a gamepad.

Now we can move on and explore new, even more unconventional ways to control the HTML game like waving your hand in front of the laptop or screaming into your microphone.

* Previous
* Overview: Control mechanisms
* Next