基本形
まず接続確認
|
1 2 3 4 5 6 7 |
window.addEventListener('gamepadconnected', (e) => { console.log('接続:', e.gamepad.id); }); window.addEventListener('gamepaddisconnected', (e) => { console.log('切断:', e.gamepad.id); }); |
毎フレーム取得
ゲームループ内で
|
1 2 3 4 5 6 7 |
const gamepads = navigator.getGamepads(); for (const pad of gamepads) { if (!pad) continue; console.log(pad.buttons[0].pressed); } |
を呼びます。
Xbox配列
ほぼ標準です。
| ボタン | index |
|---|---|
| A | 0 |
| B | 1 |
| X | 2 |
| Y | 3 |
| LB | 4 |
| RB | 5 |
| LT | 6 |
| RT | 7 |
| Select | 8 |
| Start | 9 |
| 左スティック押し込み | 10 |
| 右スティック押し込み | 11 |
| ↑ | 12 |
| ↓ | 13 |
| ← | 14 |
| → | 15 |
キーボード風に扱う
例えば
updateGamepad() {
const pad = navigator.getGamepads()[0];
if (!pad) return;
this.lFlip.on = pad.buttons[14].pressed;
this.rFlip.on = pad.buttons[15].pressed;
}
すると
- ←で左フリッパー
- →で右フリッパー
になります。
押した瞬間だけ検出
問題は
pad.buttons[0].pressed
は押している間ずっとtrueです。
キーボードのkeydown相当を作るには
this.prevButtons = [];
を用意して
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
updateGamepad() { const pad = navigator.getGamepads()[0]; if (!pad) return; pad.buttons.forEach((btn, i) => { const now = btn.pressed; const prev = this.prevButtons[i] || false; if (now && !prev) { console.log('押した瞬間', i); } if (!now && prev) { console.log('離した瞬間', i); } this.prevButtons[i] = now; }); } |
こうすると
- keydown相当
- keyup相当
が作れます。
例えば
上キー(W)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
if (now && !prev && i === 12) { if (this.state === 'LAUNCH') { this.launchHeld = true; } if (this.addState === 'ADDING') { this.launchHeld = true; } if (this.state === 'GAME_OVER') { this._restart(); } } |
上キー離す
|
1 2 3 4 5 6 7 8 9 10 11 12 |
if (!now && prev && i === 12) { if (this.state === 'LAUNCH' && this.launchHeld) { this._fire(); } if (this.addState === 'ADDING' && this.launchHeld) { this._fire(); } this.launchHeld = false; } |
アナログスティック対応
左スティック
|
1 2 |
const x = pad.axes[0]; const y = pad.axes[1]; |
範囲
左 -1.0
中央 0.0
右 1.0
例えば
|
1 2 3 4 5 6 7 |
if (x < -0.5) { this.lFlip.on = true; } if (x > 0.5) { this.rFlip.on = true; } |
navigator.getGamepads();
navigator.getGamepads()メソッドの返り値は、端末に接続されているゲームパッドの情報を格納した 配列(Array または GamepadList) です。
この配列の各要素には以下のデータが格納されています:
- 接続されている場合:
Gamepadオブジェクト - 未接続・スロットが空の場合:
null
💡 重要なポイント
- ブラウザによる違い: 配列の要素数はブラウザによって仕様が異なります。たとえば、Google Chromeでは常に固定で4つの枠(
[Gamepad0, Gamepad1, null, null]など)が用意されています。 - インデックス番号: 配列の
indexプロパティ(0~3など)は、物理的なデバイスのポートや接続順に対応しています。 - ポーリングの必要性: 状態を常に最新に保つには、このメソッドを毎フレーム呼び出す(ポーリングする)必要があります。
e.gamepad.index
const gamepads = navigator.getGamepads();
で得られる配列の添字と一致します。
例えば、
window.addEventListener('gamepadconnected', (e) => {
console.log('index=', e.gamepad.index);
});
で
index=1
と表示されたなら、
const pad = navigator.getGamepads()[1];
でそのゲームパッドにアクセスできます。
複数接続時
例えば
Xbox Controller → index=0
PS5 Controller → index=1
なら
navigator.getGamepads()
は
[
Gamepad, // index 0
Gamepad // index 1
]
となります。
切断すると穴が空く
例えば index=0 のパッドを抜くと
[
null,
Gamepad
]
のようになります。
なので、
for (const pad of navigator.getGamepads()) {
if (!pad) continue;
// 処理
}
のように null を飛ばすのが定番です。
接続時に管理する例
|
1 2 3 4 5 6 7 8 9 |
this.gamepads = []; window.addEventListener('gamepadconnected', (e) => { this.gamepads[e.gamepad.index] = e.gamepad; }); window.addEventListener('gamepaddisconnected', (e) => { delete this.gamepads[e.gamepad.index]; }); |
ただし、e.gamepad は接続時のスナップショットなので、
実際のボタン状態を読むときは毎フレーム
const pad = navigator.getGamepads()[index];
で取得し直す方が確実です。
つまり、
e.gamepad.index
は
「
navigator.getGamepads()の何番目に入っているか」
を表す番号です。
これを使えば、
- プレイヤー1 → index 0
- プレイヤー2 → index 1
のような複数人プレイにも対応できます🙋
コメント