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

Gamepad APIの利用

この翻訳は不完全です。英語から この記事を翻訳 してください。

これは実験段階の機能です。
この機能は複数のブラウザで開発中の状態にあります。互換性テーブルをチェックしてください。また、実験段階の機能の構文と挙動は、仕様変更に伴い各ブラウザの将来のバージョンで変更になる可能性があることに注意してください。

HTML5 はリッチでインタラクティブなゲームを開発するために必要なコンポーネントを多く搭載しています。 <canvas>や WebGL、<audio> に <video> などの技術は、今まで Native コードを書く必要のあった機能をサポートできるほどに成長しました。Gamepad API は開発者とデザイナーに Gamepad やコントローラーへのアクセスを提供するものです。

Gamepad APIWindow オブジェクトにGamepadとコントローラー(以下、Gamepad)の状態を読み取る新しいイベントをいくつか追加します。さらに、 Gamepad というGamepadの接続状態が得られるオブジェクトと navigator.getGamepads というGamepadの一覧を取得できるメソッドが追加されます。 

Gamepad の接続

新しい Gamepad が接続された時、アクティブなページは gamepadconnected イベントを受け取ります。ページ読み込み時にGamepadがすでに接続されている場合、Gamepad のボタンを押すなどの操作をした時に gamepadconnected イベントがアクティブなページに対して発生します。

Firefox では、 ページが見える状態でかつユーザーによる Gamepad の操作を受け付けたときにのみ、Gamepad が利用可能になります。これによって、ユーザーを特定する Fingerprinting に利用されることを防止しています。一度一つのコントローラーが操作されれば、他のコントローラーも自動で接続され利用可能になります。

以下のようにして gamepadconnected を使用します:

window.addEventListener("gamepadconnected", function(e) {
  console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
    e.gamepad.index, e.gamepad.id,
    e.gamepad.buttons.length, e.gamepad.axes.length);
});

Gamepad はそれぞれ固有の ID を gamepad プロパティの中に持っています。

Gamepad の切断

Gamepad が切断されると、Gamepad が以前に受信したデータ(例: gamepadconnected )があると、2番目のイベント(例: gamepadconnected )がフォーカスされたウィンドウにディスパッチします:

window.addEventListener("gamepaddisconnected", function(e) {
  console.log("Gamepad disconnected from index %d: %s",
    e.gamepad.index, e.gamepad.id);
});

Gamepadの index というプロパティは同じタイプの複数のコントローラーが使用されている場合であっても、システムに接続されたデバイスごとにユニークになります。 Index プロパティもまた Navigator.getGamepads() として戻される Array の index として機能します。

var gamepads = {};

function gamepadHandler(event, connecting) {
  var gamepad = event.gamepad;
  // Note:
  // gamepad === navigator.getGamepads()[gamepad.index]

  if (connecting) {
    gamepads[gamepad.index] = gamepad;
  } else {
    delete gamepads[gamepad.index];
  }
}

window.addEventListener("gamepadconnected", function(e) { gamepadHandler(e, true); }, false);
window.addEventListener("gamepaddisconnected", function(e) { gamepadHandler(e, false); }, false);

この前の例ではイベントが完了した後に gamepad プロパティがどのように保持できるかを示しています - 後でデバイスの状態照会のために使用する技術となります。

Gamepad オブジェクトの問い合わせ

ご覧のように、上述の Gamepad イベントは  Gamepad オブジェクトを返すイベントオブジェクト、上の  gamepad  のプロパティが含まれています。複数の Gamepad (すなわち、そのID ) を一度に接続される可能性があるため、イベントを発生させたのはどのゲームパッドを決定するためにこれらを使用することができます。それへの参照を保持し、それがボタンや軸のいずれかの時点で押されているかを知るために照会するなど、Gamepad オブジェクトから様々なことを行うことができます。そうすることで、多くの場合、今回と次回のイベント発生とゲームパッドの状態を知っておく必要があり、ゲームやその他のインタラクティブな Web ページであることが望ましいです。

このようなチェックを実行すると、開発者はゲームパッドゲームパッドの状態に基づいて、現在のフレームのための意思決定を行うために必要なアニメーションループ (例 : requestAnimationFrame) 一緒に Gamepad オブジェクト使用して関与する傾向があります

Navigator.getGamepads() メソッドは現在 Web ページから見える Gamepad オブジェクト ( Gamepad が繋がっていない時は毎回 null が返される ) のような、すべてのデバイスを配列として戻します。これは、同じ情報を得るために使用することができます例えば、 以下に示すように上記の最初のコード例書き換えます

window.addEventListener("gamepadconnected", function(e) {
  var gp = navigator.getGamepads()[e.gamepad.index];
  console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
    gp.index, gp.id,
    gp.buttons.length, gp.axes.length);
});

Gamepad オブジェクトの機能は以下です :

  • id: コントローラーに関する情報を含んだ文字列です。これは厳密には指定されていなく、Firefox では、コントローラのUSBベンダと製品IDを含んでいる2つの4桁16進数字列、およびドライバーから提供されるようなコントローラの名前といった3つの情報が、ダッシュ(-)により分離され含まれています。この情報はユーザーへの有益なフィードバックを表示するとともに、デバイスのコントロールのマッピングを見つけることができるようにします。
  • index: An integer that is unique for each gamepad currently connected to the system. This can be used to distinguish multiple controllers. Note that disconnecting a device and then connecting a new device may reuse the previous index.
  • mapping: A string indicating whether the browser has remapped the controls on the device to a known layout. Currently there is only one supported known layout — the standard gamepad. If the browser is able to map controls on the device to that layout the mapping property will be set to the string standard.
  • connected: A boolean indicating whether the gamepad is still connected to the system. If this is so the value is True; if not, it is False.
  • buttons: An array of GamepadButton objects representing the buttons present on the device. Each GamepadButton has a pressed and a value property:
    • The pressed property is a boolean indicating whether the button is currently pressed (true) or unpressed (false).
    • The value property is a floating point value used to enable representing analog buttons, such as the triggers on many modern gamepads. The values are normalized to the range 0.0..1.0, with 0.0 representing a button that is not pressed, and 1.0 representing a button that is fully pressed.
  • axes: An array representing the controls with axes present on the device (e.g. analog thumb sticks). Each entry in the array is a floating point value in the range -1.0 - 1.0, representing the axis position from the lowest value (-1.0) to the highest value (1.0).
  • timestamp: This returns a DOMHighResTimeStamp representing the last time the data for this gamepad was updated, allowing developers to determine if the axes and button data have been updated from the hardware. The value must be relative to the navigationStart attribute of the PerformanceTiming interface. Values are monotonically increasing, meaning that they can be compared to determine the ordering of updates, as newer values will always be greater than or equal to older values. Note that this property is not currently supported in Firefox.

Note: The Gamepad object is available on the gamepadconnected event rather than the Window object itself, for security reasons. Once we have a reference to it, we can query its properties for information about the current state of the gamepad. Behind the scenes, this object will be updated every time the gamepad's state changes.

Using button information

Let's look at a simple example that displays connection information for one gamepad (it ignores subsequent gamepad connections) and allows you to move a ball around the screen using the four gamepad buttons on the right hand side of the gamepad. You can view the demo live, and find the source code on Github.

To start with, we declare some variables: The gamepadInfo paragraph that the connection info is written into, the ball that we want to move, the start variable that acts as the ID for requestAnimation Frame, the a and b variables that act as position modifiers for moving the ball, and the shorthand variables that will be used for the requestAnimationFrame() and cancelAnimationFrame() cross browser forks.

var gamepadInfo = document.getElementById("gamepad-info");
var ball = document.getElementById("ball");
var start;
var a = 0;
var b = 0;

Next we use the gamepadconnected event to check for a gamepad being connected. When one is connected, we grab the gamepad using Navigator.getGamepads()[0], print information about the gamepad into our gamepad info div, and fire the gameLoop() function that starts the whole ball movement process up.

window.addEventListener("gamepadconnected", function(e) {
  var gp = navigator.getGamepads()[e.gamepad.index];
  gamepadInfo.innerHTML = "Gamepad connected at index " + gp.index + ": " + gp.id + ". It has " + gp.buttons.length + " buttons and " + gp.axes.length + " axes.";

  gameLoop();
});

Now we use the gamepaddisconnected event to check if the gamepad is disconnected again. If so, we stop the requestAnimationFrame() loop (see below) and revert the gamepad information back to what it was originally.

window.addEventListener("gamepaddisconnected", function(e) {
  gamepadInfo.innerHTML = "Waiting for gamepad.";

  cancelRequestAnimationFrame(start);
});

Chrome does things differently here. Instead of constantly storing the gamepad's latest state in a variable it only stores a snapshot, so to do the same thing in Chrome you have to keep polling it and then only use the Gamepad object in code when it is available. We have done this below using Window.setInterval(); once the object is available the gamepad info is outputted, the game loop is started, and the interval is cleared using Window.clearInterval(). Note that in older versions of Chrome Navigator.getGamepads() is implemented with a webkit prefix. We attempt to detect and handle both the prefixed version and the standard version of the function for backwards compatibility.

var interval;

if (!('ongamepadconnected' in window)) {
  // No gamepad events available, poll instead.
  interval = setInterval(pollGamepads, 500);
}

function pollGamepads() {
  var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
  for (var i = 0; i < gamepads.length; i++) {
    var gp = gamepads[i];
    if (gp) {
      gamepadInfo.innerHTML = "Gamepad connected at index " + gp.index + ": " + gp.id +
        ". It has " + gp.buttons.length + " buttons and " + gp.axes.length + " axes.";
      gameLoop();
      clearInterval(interval);
    }
  }
}

Now on to the main game loop. In each execution of the loop we check if one of four buttons is being pressed; if so, we update the values of the a and b movement variables appropriately, then update the left and top properties, changing their values to the current values of a and b respectively. This has the effect of moving the ball around the screen.  In current versions of Chrome (version 34 as of this writing) the button values are stored as an array of double values, instead of GamepadButton objects. This is fixed in development versions.

After all this is done, we use our requestAnimationFrame() to request the next animation frame, running gameLoop() again.

function buttonPressed(b) {
  if (typeof(b) == "object") {
    return b.pressed;
  }
  return b == 1.0;
}

function gameLoop() {
  var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
  if (!gamepads) {
    return;
  }

  var gp = gamepads[0];
  if (buttonPressed(gp.buttons[0])) {
    b--;
  } else if (buttonPressed(gp.buttons[2])) {
    b++;
  }
  if (buttonPressed(gp.buttons[1])) {
    a++;
  } else if (buttonPressed(gp.buttons[3])) {
    a--;
  }

  ball.style.left = a * 2 + "px";
  ball.style.top = b * 2 + "px";

  start = requestAnimationFrame(gameLoop);
}

Using axes information

TBD (basically the same, except using axes[i] rather than button[i].value for both Firefox and Chrome.)

Complete example: Displaying gamepad state

This example shows how to use the Gamepad object, as well as the gamepadconnected and gamepaddisconnected events in order to display the state of all gamepads connected to the system. You can find a working demo and look at the full source code on Github.

var haveEvents = 'ongamepadconnected' in window;
var controllers = {};

function connecthandler(e) {
  addgamepad(e.gamepad);
}

function addgamepad(gamepad) {
  controllers[gamepad.index] = gamepad;

  var d = document.createElement("div");
  d.setAttribute("id", "controller" + gamepad.index);

  var t = document.createElement("h1");
  t.appendChild(document.createTextNode("gamepad: " + gamepad.id));
  d.appendChild(t);

  var b = document.createElement("div");
  b.className = "buttons";
  for (var i = 0; i < gamepad.buttons.length; i++) {
    var e = document.createElement("span");
    e.className = "button";
    //e.id = "b" + i;
    e.innerHTML = i;
    b.appendChild(e);
  }

  d.appendChild(b);

  var a = document.createElement("div");
  a.className = "axes";

  for (var i = 0; i < gamepad.axes.length; i++) {
    var p = document.createElement("progress");
    p.className = "axis";
    //p.id = "a" + i;
    p.setAttribute("max", "2");
    p.setAttribute("value", "1");
    p.innerHTML = i;
    a.appendChild(p);
  }

  d.appendChild(a);

  // See https://github.com/luser/gamepadtest/blob/master/index.html
  var start = document.getElementById("start");
  if (start) {
    start.style.display = "none";
  }

  document.body.appendChild(d);
  requestAnimationFrame(updateStatus);
}

function disconnecthandler(e) {
  removegamepad(e.gamepad);
}

function removegamepad(gamepad) {
  var d = document.getElementById("controller" + gamepad.index);
  document.body.removeChild(d);
  delete controllers[gamepad.index];
}

function updateStatus() {
  if (!haveEvents) {
    scangamepads();
  }

  var i = 0;
  var j;

  for (j in controllers) {
    var controller = controllers[j];
    var d = document.getElementById("controller" + j);
    var buttons = d.getElementsByClassName("button");

    for (i = 0; i < controller.buttons.length; i++) {
      var b = buttons[i];
      var val = controller.buttons[i];
      var pressed = val == 1.0;
      if (typeof(val) == "object") {
        pressed = val.pressed;
        val = val.value;
      }

      var pct = Math.round(val * 100) + "%";
      b.style.backgroundSize = pct + " " + pct;

      if (pressed) {
        b.className = "button pressed";
      } else {
        b.className = "button";
      }
    }

    var axes = d.getElementsByClassName("axis");
    for (i = 0; i < controller.axes.length; i++) {
      var a = axes[i];
      a.innerHTML = i + ": " + controller.axes[i].toFixed(4);
      a.setAttribute("value", controller.axes[i] + 1);
    }
  }

  requestAnimationFrame(updateStatus);
}

function scangamepads() {
  var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
  for (var i = 0; i < gamepads.length; i++) {
    if (gamepads[i]) {
      if (gamepads[i].index in controllers) {
        controllers[gamepads[i].index] = gamepads[i];
      } else {
        addgamepad(gamepads[i]);
      }
    }
  }
}


window.addEventListener("gamepadconnected", connecthandler);
window.addEventListener("gamepaddisconnected", disconnecthandler);

if (!haveEvents) {
  setInterval(scangamepads, 500);
}

Specifications

Specification Status Comment
Gamepad
Gamepad の定義
草案 Initial defintion

Browser compatibility

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
General support 21.0 webkit
35.0
29.0 (29.0) [1] 未サポート 15.0 webkit
22.0
未サポート
Feature Android Firefox Mobile (Gecko) IE Phone Opera Mobile Safari Mobile
General support 未サポート 32.0 (32.0) 未サポート 未サポート 未サポート

[1] Starting with Firefox 24, the Gamepad API was available behind a preference. You can enable it in those versions by loading about:config and setting the dom.gamepad.enabled preference to true.

ドキュメントのタグと貢献者

 このページの貢献者: T.Ukegawa, mzyy94
 最終更新者: T.Ukegawa,