【Gamepad API】Logicoolゲームパッドの操作イベントをJSで取得する

5 min read読了の目安(約5100字

概要

Logicoolのゲームパッドを買ったので、コントローラーで操作できるWebの何かを作ろうと思い
JavaScriptでボタンのイベント取得する方法を備忘録としてまとめます。

動作環境

LogicoolをMacで動かす

LogicoolはWindows向けのゲームパッドですが、ドライバーを入れればMacでも動作しました。
↓ドライバーの入れ方はこちらを参考にさせていただきました。

https://www.noname774.xyz/entry/2020/11/01/161703

ドライバーを正常にインストールできると画像のようにシステム環境設定にゲームパッドのメニューが表示されます。

開くとこのような感じでゲームパッドの動作テスト等できる画面になります。
ゲームパッドのボタンを適当に押してみて、この画面でシミュレーションができれば正常にMacに接続されていることがわかります。

接続イベント

ボタン押下イベントの取得の前にまずゲームパッドが接続されているかを取得する必要があります。
gamepadconnectedイベントを利用して取得してみます。

window.addEventListener("gamepadconnected", (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 connected at index 0: Xbox 360 Wired Controller (Vendor: 046d Product: c21d). 15 buttons, 6 axes.

この接続確認ができてからボタンの取得などを行います。

ボタン押下の取得

ボタン押下に関しては特にイベントが用意されていなさそうなのでrequestAnimationFrameで毎フレーム確認しに行く方法で実装します。

const buttonPressed = (button) => {
  if (typeof button == "object") {
    return button.pressed;
  }
  return button == 1.0;
};

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

  const gp = gamepads[0];
  if (buttonPressed(gp.buttons[0])) {
    console.log("Aボタン押下")
  };

  requestAnimationFrame(gameLoop);
};

MDNのドキュメントを参考にしています。
ここではシンプルにAボタンを押下時にコンソールへメッセージを表示するようにしています。
(このgameLoop関数は先ほどのwindow.addEventListener("gamepadconnected", ...の中で呼びます)

ボタンの対応表

ボタンはgamepads.buttons、アナログスティック系(次章)はgamepads.axesでそれぞれのArrayが取得できます。

Gamepad APIのドキュメントにはXBox 360公式ゲームパッドのボタンレイアウトが掲載されていました。

buttons: [
'DPad-Up','DPad-Down','DPad-Left','DPad-Right',
'Start','Back','Axis-Left','Axis-Right',
'LB','RB','Power','A','B','X','Y',
],

ただ、今回使うLogicoolのゲームパッドは異なりましたので以下にまとめています。(他のゲームパッドはまた異なると思いますのでご注意ください)

試してみると左側のアナログスティックがボタンだったり、スティック側に十字キーが入ってたりもしますが…この辺は安価の非公式のゲームパッドなのでしょうがないですね。

Logicoolボタン 配列番号
A 0
B 1
X 2
Y 3
LB 4
RB 5
左スティック押し込み 6
右スティック押し込み 7
START 8
BACK 9
中央Logicoolボタン 10
左スティック上 11
左スティック下 12
左スティック左 13
左スティック右 14

スティックの入力取得

booleanを返すボタンに対して、スティック(LT, RT含む)は-1〜1の値を返します。

Logicoolスティック 配列番号
十字キー左 0 -1
十字キー右 0 1
十字キー上 1 -1
十字キー下 1 1
LT 2 1
右スティック左 3 -1
右スティック右 3 1
右スティック上 4 -1
右スティック下 4 1
RT 5 1

操作デモ

今回はボタンの入力を取得して画像が動くデモを作ってみます。

<head>
  <style>
    body {
      margin: 0;
      background: black;
    }
    .kuma {
      position: fixed;
      top: calc(50% - 50px);
      left: calc(50% - 50px);
      width: 100px;
      height: 100px;
    }
  </style>
</head>
<body>
  <img id="kuma" class="kuma" src="kuma.gif" />
  <script>
    const kuma = document.getElementById("kuma");
    let start;
    let a = 0;
    let b = 0;

    const buttonPressed = (button) => {
      if (typeof button == "object") {
        return button.pressed;
      }
      return button == 1.0;
    };

    const gameLoop = () => {
      const gamepads = navigator.getGamepads
        ? navigator.getGamepads()
        : navigator.webkitGetGamepads
        ? navigator.webkitGetGamepads
        : [];
      if (!gamepads) {
        return;
      }
  
      const gp = gamepads[0];
      if (buttonPressed(gp.buttons[3])) {
        b--;
      } else if (buttonPressed(gp.buttons[0])) {
        b++;
      }
      if (buttonPressed(gp.buttons[1])) {
        a++;
      } else if (buttonPressed(gp.buttons[2])) {
        a--;
      }

      const kumaLeft = kuma.getBoundingClientRect().left;
      const kumaTop = kuma.getBoundingClientRect().top;
      const speed = 5;

      kuma.style.left = kumaLeft + a * speed + "px";
      kuma.style.top = kumaTop + b * speed + "px";
      a = 0;
      b = 0;

      start = requestAnimationFrame(gameLoop);
    };

    window.addEventListener("gamepadconnected", (e) => {
      gameLoop();
    });
  </script>
</body>

無事動かすことができました。

参考

https://developer.mozilla.org/ja/docs/Web/API/Gamepad_API/Using_the_Gamepad_API
https://developer.mozilla.org/ja/docs/Games/Techniques/Controls_Gamepad_API