コンテンツにスキップするには Enter キーを押してください

ゲームパッドでブラウザゲームを操作できるGamepad APIが楽しい【俺聞け11】

Webに少しでも関連させればどんなことでも自由に発表していい上にオーディエンスがみんな優しい、日本一発表の敷居が低いイベント「俺聞け11」で、ゲームパッドでブラウザゲームを操作できるGamepad APIについて紹介・発表してきました。

Gamepad APIはその名のとおり、JavaScriptでゲームパッドからの入力を受け取ることが出来るAPIです。
Firefox, Chrome, Operaは既に対応済みですし、IEでも次期バージョンでは対応される予定になっています。
Safari…? ちょっと知らない子ですね…。

発表の時に使ったスライドは、TypeScript+Knockout.jsのWebアプリとして作りました。
ゲームパッドで操作できるようにしているので、発表スライドであると同時にデモも兼ねています。
BitBucketに非公開リポジトリを置いて管理してはいるんですが、権利上の問題でお見せすることはできません…。

代わりに、Gamepad APIを使うサンプルコードと、はまりがちな落とし穴をざっくりですが紹介します。

function checkGamepads(){
    var gamepadList = navigator.getGamepads();
    for(var i=0; i<gamepadList.length; i++){
        var gamepad = gamepadList[i];
        // ブラウザによっては配列にundefinedが入っている場合がある
        if(gamepad){
            // Gamepad#id string ゲームパッドのデバイス名等
            console.log(gamepad.id)
            // => "Xbox 360 Controller (XInput STANDARD GAMEPAD)"

            // Gamepad#mapping string キーマップピングが標準かどうか
            // ("standard"…標準 ""(空文字列)…独自)
            console.log(gamepad.mapping);
            // => "standard"

            // Gamepad#axes double[] 方向キーの入力方向と強さの配列
            // (0が真ん中、-1に近づくと上/左方向、1に近づくと下/右方向)
            // キーに触ってないときもちょうど0になるわけではなく0.03くらいの値になるので注意
            console.log(gamepad.axes);
            // => [
            //   -0.9935354739878914,
            //   0.03535467849054890,
            //   -0.5789490786325657,
            //   0.6474398798231042  
            // ]

            // Gamepad#buttons GamepadButton[] -> ボタンオブジェクトの配列
            for(var i=0; i<gamepad.buttons.length; i++){
                var button = gamepad.buttons[i];
                // GamepadButton#pressed bool ボタンが押されているかどうか
                console.log(button.pressed);
                // => true;

                // GamepadButton#value double (アナログ入力の場合)押されている強さ(0~1)
                // アナログ入力でない場合は0/1のみ
                console.log(button.value);
                // => 0.7893214690458746648
            }
        }
    }
    window.requestAnimationFrame(checkGamepads);
}
// ボタンが押されたり、方向キーの入力強さが変わったりしてもイベントは発生しないので、
// requestAnimationFrameを使い、ブラウザの描画更新タイミングごとに値をチェックする
window.requestAnimationFrame(checkGamepads);

標準のキーマッピングとは


Xbox360コントローラやPlayStation3のDualShock3で採用されている、ABXY・LR×2・セレクト・スタート・アナログ方向キー×2・上下左右・センターというキーマッピングがGamepad APIにおける「標準」として扱われています。

各ボタンと、Gamepad#Buttons配列におけるインデックスの対応は次のとおりです

インデックス ボタン
0 A (任天堂系のコントローラとはAとB、XとYの位置が逆)
1 B
2 X
3 Y
4 LB PS系・任天堂系だとLR1
5 RB
6 LT 〃LR2
アナログ入力対応
7 RT
8 セレクト
9 スタート
10 アナログ方向キー(左)
11 アナログ方向キー(右)
12 十字キー上 DualShock3だとアナログ入力対応
13 十字キー下
14 十字キー左
15 十字キー右
16 センター Xbox360コントローラならXboxロゴ、DualShock3ならPSロゴボタン

また、アナログ方向キーの強さが格納されているaxes配列の対応は次のとおりです

インデックス キー
0 左側キー左右
1 左側キー上下
2 右側キー左右
3 右側キー上下

落とし穴たくさん

Windowsだとセンターボタン(Xox360コントローラで言うとXboxロゴのボタン)が反応しない

よくわかりません。Macでは反応しました。

WindowsだとDualShock3が使えない


ドライバが無いからだと思います。なんでMacでは普通に動くんだろう…。

MacのFirefoxだとXbox360コントローラが認識されない

なんか認識されませんでした。Chromeでは認識されたのでドライバのせいではないと思います。Windowsでは大丈夫でした。

MacのFirefoxだとDualShock3が標準キーマッピング扱いにならない

なぜか独自キーマッピングとして扱われてしまいます。

キーマッピングが標準ではない場合、どの数字がどのボタンに対応しているかは製品によってバラバラ

市販のPC用ゲームパッドの中には、往年の人気ゲーム機のコントローラを模したものもあります。
こういうゲームパッドの場合、ボタン数が少ないので標準のキーマッピングに当てはめることができません。
そういう場合はデバイスごとの独自キーマッピングになるのですが、独自キーマッピングの場合、どのインデックスがどのボタンに対応しているかを知ることはできません。

いちいち対応するのも面倒なので、標準ではないキーマッピングのゲームパッドを使っている人にはキーコンフィグを作って自分で設定してもらうようにするか、あるいはばっさり切り捨ててしまうのも手です。

Gamepad#idがOS・ブラウザによってバラバラ

Gamepad#idで取得できる文字列にはベンダIDとプロダクトIDが埋め込まれているので、独自キーマッピングのデバイスでも個別対応しようと思えばできなくもないのですが、その値がOS・ブラウザごとにバラバラなので使い物になりません

OS ブラウザ ID
Windows Chrome
"USB 2A8K  GamePad          (Vendor: 040b Product: 6530)"
Firefox
"040b-6530-USB 2A8K  GamePad          "
Mac Chrome
"USB 2A8K  GamePad (Vendor: 040b Product: 6530)"
Firefox
"40b-6530-USB 2A8K  GamePad"

どうしてこうなった

余談

プレゼン中に鳴っていた効果音について

プレゼンの文字を1文字ずつ表示するときや、ページ送りをするときに、「プププププププ…」「ピッ」って感じのファミコン風の効果音が流れるようにしていました。
プレゼン中は特に言及しなかったんですが、あの音は、実はブラウザ上でリアルタイムに生成していました。
音声ファイルは一切使用していません。

JavaScriptで任意の音声波形を生成したり、エフェクトをかけたりして音声を再生できる、Web Audio APIというAPIを使っています。
ゲームの効果音として使う場合、音声ファイルを事前に読み込んでおき、適切なタイミングで再生するのが普通だと思うんですが、今回は音声が単純なのでそれっぽい周波数の矩形波をリアルタイムで生成して再生していました。

コナミコマンドについて

プレゼンの終盤に、ゲームパッドでコナミコマンドを入力すると、画面上部に表示されていたスコアがカンストする(9999999点)という小ネタをやりました。
これは、ゲームパッドで入力されたキーの履歴を最新10回分記録しておいて、その履歴がコナミコマンドと一致したら実行、という仕組みで実装しました。
これはそのうちサンプルコードかライブラリか何かを公開するつもりです。

参考資料とか


1件のコメント

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です