JavaScriptでゲームパッド対応を実装した話
ブラウザゲームのコントローラー入力をリバースエンジニアリングするために、週末を丸ごと費やしました。深夜2時、私は空の配列をただ見つめていました。何も表示されなかったのです。
その経験から、どんなチュートリアルよりもブラウザAPIとハードウェアポーリングについて多くのことを学びました。
ウェブゲームは成長しています。WebGLやWebGPUの登場により、ブラウザゲームは今や非常に高いクオリティに達しています。プレイヤーは、XboxやPS5のコントローラーを接続すれば、すぐに使えるようになることを期待しています。
ゲーム、アクセシビリティツール、あるいはカスタムダッシュボードを開発しているなら、HTML5 Gamepad APIが必要です。これはChrome、Firefox、Edgeで動作し、ライブラリも必要ありません。
学んだことは以下の通りです:
- APIは接続に関してはイベント駆動型ですが、入力に関してはポーリングベースです。
- 入力を継続的に読み取るには、
requestAnimationFrameを使用する必要があります。 - ユーザーがボタンを押すまで、ブラウザはコントローラーを表示しません。これはハードウェア・フィンガープリントを防ぐためのプライバシー機能です。
データの管理:
gamepad.mappingを使用して「standard」レイアウトかどうかを確認します。標準マッピングを使用すれば、ボタン0が常に下側のフェイスボタンであることを保証できます。gamepad.idを確認して、ベンダーIDとプロダクトIDを特定します。これにより、Sony DualSenseのような特定のハードウェアを識別できます。- 常にデッドゾーン・フィルターを実装してください。アナログスティックが完全に中央に止まることはありません。デッドゾーンがないと、キャラクターやカメラが常にドリフトしてしまいます。
デッドゾーンを処理する簡単な方法: Math.abs(axis) > 0.1 であれば、その値を使用する。
ハプティックフィードバック:
gamepad.vibrationActuator.playEffectを使用して、振動を発生させることができます。これはChromeとEdgeでうまく動作します。Firefoxは部分的にサポートしていますが、Safariはサポートしていません。
プロジェクトにおける重要なポイント:
gamepadconnectedとgamepaddisconnectedイベントをリッスンします。- 「押し下げ(press)」と「長押し(hold)」を区別するために、前回のボタンの状態を追跡します。
- スティックの微細な動きを無視するために、デッドゾーンのしきい値を使用します。
- 複数のブラウザでテストしてください。
navigator.getGamepads()はスナップショットを返すということを忘れないでください。最新のデータを取得するには、ループ内でこれを呼び出す必要があります。
コードを書く前にハードウェアをテストしたい場合は、GamepadTester.proを使用してください。生のAPIデータと軸の値をリアルタイムで表示してくれます。
