Tuesday, August 16, 2016

Simple Xbox Controller Input in HTML5 With PxGamepad

Gaming on the web has come a long way with HTML5 technologies like Canvas, WebGL, and WebAudio. It's now possible to produce high-fidelity graphics and sound within the browser. However, to provide a true gaming experience, you need input devices designed for gaming. The Gamepad API is a proposed standard of the W3C, and is designed to provide a consistent API across browsers.

The Gamepad API allows users to connect devices like an Xbox Controller to a computer and use them for browser-based experiences! Our helper class, PxGamepad, then maps the button and axis indices to the more familiar names as labeled on the Xbox controller.

If you have a gamepad, try plugging it into your computer, click the picture of the Xbox controller below, and press a button. You’ll see the controller light up to mirror each movement you make!

This tutorial is the third in a series on Flight Arcade—built to demonstrate what’s possible on the web platform and in the new Microsoft Edge browser and EdgeHTML rendering engine. You can find the first two articles on WebGL and Web API, plus interactive code and examples for this article, at flightarcade.com and here on Tuts+.

Flexible API

The Gamepad API is intelligently designed with flexibility in mind. At a basic level, it provides access to buttons and axes. Button values range from 0 to 1 inclusive, while axes range from -1 to 1 inclusive. All values are normalized to these ranges so developers can expect consistent behavior between devices.

The Gamepad object provides detailed information about the manufacturer and model of the connected gamepad. More useful is a “mapping” property which describes the general type of gamepad. Currently the only supported mapping is “standard”, which corresponds to the controller layout used by many popular game consoles like the Xbox.

The standard controller mapping has two sticks, each of which is represented by two axes (x and y). It also includes a D-pad, four game buttons, top buttons, and triggers: all represented as buttons in the
Gamepad API.

Current Xbox controllers report button state as either 0 (normal state) or 1 (pressed). However, you could imagine that future controllers could report the amount of force applied to each button press.

The Xbox D-pad also reports discrete values (0 or 1), but the sticks provide continuous values across the entire axis range (-1 to 1). This additional precision makes it much easier to fly the airplane in our Flight Arcade missions.

PxGamepad

The array of buttons and axes provided by the Gamepad API is forward thinking and perfect as a low-level API. However, when writing a game, it’s nice to have a higher-level representation of a standard gamepad like the Xbox One controller. We created a helper class named PxGamepad that maps the button and axis indices to the more familiar names as labeled on the Xbox controller.

I'll walk through a few interesting pieces of the library, but the full source code (MIT License) is available on GitHub.

The standard Gamepad API provides button state as an array of buttons. Again, this API is designed for flexibility, allowing controllers with various button counts. However, when writing a game, it's much easier to write and read code that uses the standard mapped button names.

For example, with the HTML5 Gamepad API, here is the code to check whether the left trigger is currently pressed:
  1. function isLeftTriggerPressed() {
  2.      
  3.     var leftTrigger = gamepad.buttons[6];
  4.     if (!leftTrigger) {
  5.         return false;
  6.     }
  7.  
  8.     if (typeof(leftTrigger) === "object") {
  9.         return leftTrigger.pressed;
  10.     }
  11.  
  12.     return (leftTrigger === 1.0);
  13. }
The PxGamepad class contains an update method that will gather the state for all the standard mapped buttons and axes. So determining whether the leftTrigger is pressed is as simple as accessing a Boolean property:
  1. var isPressed = pxgamepad.buttons.leftTrigger;
Axes in the standard Gamepad API are also provided as an array of numerical values. For example, here is the code to get the normalized x and y values for the left stick:
  1. var leftStickX = gamepad.axes[0];
  2. var leftStickY = gamepad.axes[1];
The D-pad is a special case, because it is considered to be a set of four buttons by the HTML5 Gamepad API (indices 121314, and 15). However, it's common for developers to allow the D-pad to be used in the same way as one of the sticks. PxGamepad provides button infomation for the D-pad, but also synthesizes axis information as though the D-pad were a stick:
  1. var dpadX = pxgamepad.dpad.x;
  2. var dpadY = pxgamepad.dpad.y;
Another limitation of the HTML5 Gamepad API is that is doesn't provide button-level events. It's common for a game developer to want to activate a single event for a button press. In Flight Arcade, the ignition and brake buttons are good examples. PxGamepad watches button state and allows callers to register for notifications on button release.
  1. gamepad.on('rightTrigger', function() {
  2.     console.log('right trigger fired!');
  3. });
 Here is the full list of named buttons supported by PxGamepad:
  • a
  • b
  • x
  • y
  • leftTop
  • rightTop
  • leftTrigger
  • rightTrigger
  • select
  • start
  • leftStick
  • rightStick
  • dpadUp
  • dpadDown
  • dpadLeft
  • dpadRight
Obtaining the Current Gamepad

There are two methods for retrieving the gamepad object. The Gamepad API adds a method to the navigator object named getGamepads(), which returns an array of all connected gamepads. There are also new gamepadconnected and gamepaddisconnected events that are fired whenever a new gamepad has been connected or disconnected. For example, here is how the PxGamepad helper stores the last connected gamepad:
  1. // start listening for gamepad connection events
  2. PxGamepad.prototype.start = function() {
  3.  
  4.     this.reset();
  5.  
  6.     this.listeners = {
  7.         'gamepadconnected': jQuery.proxy(function(e) {
  8.             var gamepad = e.originalEvent.gamepad;
  9.             if (gamepad.mapping === 'standard') {
  10.                 this.connectedGamepad = gamepad;
  11.             }
  12.         }),
  13.         'gamepaddisconnected': jQuery.proxy(function(e) {
  14.             var gamepad = e.originalEvent.gamepad;
  15.             if (this.connectedGamepad === gamepad) {
  16.                 this.connectedGamepad = null;
  17.             }
  18.         })
  19.     };
  20.  
  21.     jQuery(window).on(this.listeners);
  22. };
And here is the helper to retrieve the first standard gamepad using the navigator.getGamepads() API:
  1. // helper to retrieve the currently connected gamepad
  2. PxGamepad.prototype.getGamepad = function() {
  3.      
  4.     // default to connected gamepad
  5.     var gp = this.connectedGamepad;
  6.      
  7.     if (!gp) {
  8.  
  9.         // fetch all available gamepads
  10.         var gamepads;
  11.         if (navigator.getGamepads) {
  12.             gamepads = navigator.getGamepads();
  13.         } else if (navigator.webkitGetGamepads) {
  14.             gamepads = navigator.webkitGetGamepads();    
  15.         }
  16.  
  17.         // look for a standard mapped gamepad
  18.         if (gamepads) {
  19.             for (var i = 0, len = gamepads.length; i < len; i++) {
  20.                 if (gamepads[i].mapping === 'standard') {
  21.                     gp = gamepads[i];
  22.                     break;
  23.                 }
  24.             }
  25.         }
  26.     }
  27.  
  28.     return gp;
  29. };
The PxGamepad helper class is designed for the simple scenario where a single user is playing a game with a standard mapped gamepad. The latest browsers, like Microsoft Edge, fully support the W3C Gamepad API. However, older versions of some other browsers only supported pieces of the emerging specification. PxGamepad listens for the gamepadconnected events and falls back to querying for the list of all gamepads if needed.

The Future of Gamepad

While PxGamepad is focused on the simple, most common scenario, the Gamepad API is fully capable of supporting multiple players, each with their own gamepad. One possible improvement for PxGamepad might be to provide a manager-style class which tracks connection of multiple gamepads and maps them to multiple players in a game. Another might be to allow users to remap or customize the button functions on their gamepads.

We're also excited about the potential of the Gamepad API for non-game scenarios. With the rise of WebGL, we're seeing a variety of innovative uses for 3D on the web. That might mean exploring the Mt. Everest region in 3D with GlacierWorks, or viewing the Assyrian Collection of the British Museum thanks to CyArk's efforts to digitally preserve important world sites and artefacts.

During the development of Flight Arcade, we frequently used Blender and other 3D tools to process models for Babylon.js. Some developers and artists use a device called a 3D mouse to help manipulate and navigate 3D models. These devices track movement of a single knob through six axes! They make it really easy and quick to manipulate models. Beyond gaming, they're used in a variety of interesting applications from engineering to medical imaging. While adding gamepad support to Flight Arcade, we were surprised to learn that the Gamepad API detected our 3D SpaceMouse and provided movement data for all six axes!

It's exciting to imagine all the possibilities that the new Gamepad API offers. Now is a great time to experiment with the new Gamepad API and add precision control and a lot of fun to your next game or application!
Written by Robby Ingebretsen

If you found this post interesting, follow and support us.
Suggest for you:

Mastering HTML5 Game Development

Code Your First Game: Arcade Classic in JavaScript on Canvas

Mobile Game Development Gamemaker Studio

Game Development Crash Course w/Corona SDK: You Can Do It!

Make VR Games in Unity with C# - Cardboard, Gear VR, Oculus


No comments:

Post a Comment