Subterranean Flower

GamepadAPIでブラウザ上からゲームパッドの入力を取得する

Author
古都こと
ふよんとえんよぷよぐやま!!!

GamepadAPIを利用することで、ブラウザ上からゲームパッドにアクセスできるようになります。GamepadAPIはChrome、Firefox、Edgeが対応しています。

ゲームパッドAPIの利用方法

接続・切断を検出する

入力を検知するには、まずゲームパッドの接続を検出する必要があります。ゲームパッドの接続はgamepadconnectedイベント、切断はgamepaddisconnectedイベントで検出することができます。

ブラウザによっては、ゲームパッドを接続してページを開いただけではgamepadconnectedイベントが発火されないことがあります。これはセキュリティのためです。その場合は何かしらのボタンをおすことでgamepadconnectedイベントが発火されます。

addEventListener("gamepadconnected", (e) => {
    let gamepad = e.gamepad;
    console.log("ゲームパッドが接続されました。");
    console.log("Index: %d, ID: %s, Type: %s",
                gamepad.index, gamepad.id, gamepad.mapping);
});

addEventListener("gamepaddisconnected", (e) => {
    let gamepad = e.gamepad;
    console.log("ゲームパッドが切断されました。");
    console.log("Index: %d, ID: %s, Type: %s",
                gamepad.index, gamepad.id, gamepad.mapping);
});

現時点でのゲームパッドの入力情報を取得する

navigator.getGamepadsメソッドを使用することで、現時点で接続されている全てのゲームパッドの情報を取得することができます。navigator.getGamepadsメソッドはGamepadオブジェクトの配列を返すので、index値を利用して任意のゲームパッドの情報を取り出します。

addEventListener("gamepadconnected", (e) => {
    let connectedGamepadIndex = e.gamepad.index;
    let gamepads = navigator.getGamepads();
    let gp = gamepads[connectedGamepadIndex];
});

ボタンが押されているかどうかを確認する

Gamepadオブジェクトはbuttons配列を持ちます。buttons配列にはGamepadButtonオブジェクトが格納されています。GamepadButtonオブジェクトは押下されているかどうかを表すpressedと、押されている強さを表すvalueプロパティを持ちます。通常、valueプロパティは0.0か1.0の値しか取りませんが、感圧式ボタンやLRトリガーなどの場合は0.0〜1.0の間の値を取ります。

let connectedGamepadIndex;
let loopID;

addEventListener("gamepadconnected", (e) => {
    connectedGamepadIndex = e.gamepad.index;
    loopID = requestAnimationFrame(loop);
});

addEventListener("gamepaddisconnected", (e) => {
    connectedGamepadIndex = null;
    cancelAnimationFrame(loopID);
});

// standardタイプのコントローラのマッピングです。
const BUTTON_A_INDEX     = 0;
const BUTTON_B_INDEX     = 1;
const BUTTON_X_INDEX     = 2;
const BUTTON_Y_INDEX     = 3;
const BUTTON_LB_INDEX    = 4;
const BUTTON_RB_INDEX    = 5;
const BUTTON_LT_INDEX    = 6;
const BUTTON_RT_INDEX    = 7;
const BUTTON_BACK_INDEX  = 8;
const BUTTON_START_INDEX = 9;
const BUTTON_L3_INDEX    = 10;
const BUTTON_R3_INDEX    = 11;
const BUTTON_UP_INDEX    = 12;
const BUTTON_DOWN_INDEX  = 13;
const BUTTON_LEFT_INDEX  = 14;
const BUTTON_RIGHT_INDEX = 15;
const BUTTON_HOME_INDEX  = 16;

function loop(timestamp) {
    // ゲームパッドの入力情報を毎フレーム取得します。
    let gamepads = navigator.getGamepads();
    let gp = gamepads[connectedGamepadIndex];

    // ボタンが押されているかどうかを取得します。
    let aButton = gp.buttons[BUTTON_A_INDEX];
    if(aButton.pressed) {
        console.log(`Aボタンが押されました。value:${aButton.value}`);
    }

    requestAnimationFrame(loop);
}

スティックが倒されているかどうかを確認する

スティックについても同様です。Gamepadオブジェクトはaxesプロパティを持ちます。axesプロパティは配列で、0.0から1.0のdouble値が入っています。

let connectedGamepadIndex;
let loopID;

addEventListener("gamepadconnected", (e) => {
    connectedGamepadIndex = e.gamepad.index;
    loopID = requestAnimationFrame(loop);
});

addEventListener("gamepaddisconnected", (e) => {
    connectedGamepadIndex = null;
    cancelAnimationFrame(loopID);
});

// standardタイプのコントローラのマッピングです。
const AXIS_L_HORIZONTAL_INDEX = 0;
const AXIS_L_VERTICAL_INDEX   = 1;
const AXIS_R_HORIZONTAL_INDEX = 2;
const AXIS_R_VERTICAL_INDEX   = 3;

function loop(timestamp) {
    // ゲームパッドの入力情報を毎フレーム取得します。
    let gamepads = navigator.getGamepads();
    let gp = gamepads[connectedGamepadIndex];

    // スティックが倒されているかどうかを取得します。
    let leftAxisHorizontal = gp.axes[AXIS_L_HORIZONTAL_INDEX];
    let leftAxisVertical = gp.axes[AXIS_L_HORIZONTAL_INDEX];
    if(leftAxisHorizontal > 0 || leftAxisVertical > 0) {
        console.log(`Lスティックが倒されました。Hor:${leftAxisHorizontal}, Ver:${leftAxisVertical}`);
    }

    requestAnimationFrame(loop);
}