前々から気になっていたのですが、最近あんまりネットで効かなくなったGamepadAPI
を試してみます。
【公式情報】 w3c.github.io
公式のW3Cな情報でもいいのですが、Mozillaのサイトのほうが情報がわかりやすいので以下を参照しながらコーディングをしてみようと思います。
【参考】
参考をみるとこの様に書かれています。
Gamepad API は開発者とデザイナーに Gamepad やコントローラーへのアクセスを提供するものです。Gamepad API は Window オブジェクトにGamepadとコントローラー(以下、Gamepad)の状態を読み取る新しいイベントをいくつか追加します。
ではこれを見ながら最小限のコーディングをしていきます。
Webに公開できるようにGithubにUPしてみました。ここでもソースコードおいてみますのでご利用ください。
ソースコード
【index.html】
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> .buttons, .axes { padding: 1em; } .axis { min-width: 400px; margin: 1em; } .button { padding: 1em; border: 2px solid black; background-color: rgb(255, 255, 128); } .pressed { border: 2px solid black; background-color: red; } </style> </head> <body> <h2 id="start">Gamepadを接続してなにかボタンを押すとスタートします。</h2> <script type="text/javascript" src="gamepad.js"></script> </body> </html>
【gamepad.js】
var haveEvents = 'GamepadEvent' in window; var controllers = {}; var rAF = window.requestAnimationFrame; function connectHandler(e) { addGamepad(e.gamepad); } function addGamepad(gamepad) { // gamepadのArrayを作成 controllers[gamepad.index] = gamepad; // HTMLへ接続されたGamepad毎の要素を追加(複数のgamepadにも対応) var d = document.createElement("div"); d.setAttribute("id", "controller" + gamepad.index);//idはpadの番号がついた形式 var t = document.createElement("h2"); t.appendChild(document.createTextNode("接続Gamepad情報: ")); d.appendChild(t); var info = document.createElement("h1"); info.appendChild(document.createTextNode(gamepad.id)); d.appendChild(info); //Gamepadコントロール要素(ボタンなど)表示部分 var b = document.createElement("div"); b.className = "buttons"; var t = document.createElement("h2"); t.appendChild(document.createTextNode("ボタンコントロール情報: ")); b.appendChild(t); for (var i = 0; i < gamepad.buttons.length; i++) { var e = document.createElement("span"); e.className = "button"; //e.id = "b" + i; e.innerHTML = i; b.appendChild(e); } d.appendChild(b); //Gamepadコントロール要素(アナログジョイなど)表示部分 var a = document.createElement("div"); a.className = "axes"; var t = document.createElement("h2"); t.appendChild(document.createTextNode("アナログコントロール情報: ")); a.appendChild(t); for (i = 0; i < gamepad.axes.length; i++) { c = document.createElement("h3"); c.appendChild(document.createTextNode("axis" + i)); a.appendChild(c); e = document.createElement("meter"); e.className = "axis"; //e.id = "a" + i; e.setAttribute("min", "-1"); e.setAttribute("max", "1"); e.setAttribute("value", "0"); e.innerHTML = i; a.appendChild(e); } d.appendChild(a); document.getElementById("start").style.display = "none"; document.body.appendChild(d); rAF(updateStatus); } function disconnectHandler(e) { removeGamepad(e.gamepad); } function removeGamepad(gamepad) { var d = document.getElementById("controller" + gamepad.index); document.body.removeChild(d); delete controllers[gamepad.index]; } function updateStatus() { scanGamepads(); for (j in controllers) { var controller = controllers[j]; var d = document.getElementById("controller" + j); var buttons = d.getElementsByClassName("button"); //ボタン情報の状態取得 for (var i = 0; i < controller.buttons.length; i++) { var b = buttons[i]; var val = controller.buttons[i]; var pressed = val == 1.0; if (typeof (val) == "object") { pressed = val.pressed; val = val.value; } var pct = Math.round(val * 100) + "%"; b.style.backgroundSize = pct + " " + pct; if (pressed) { b.className = "button pressed"; } else { b.className = "button"; } } //アナログコントロール情報の状態取得 var axes = d.getElementsByClassName("axis"); for (var i = 0; i < controller.axes.length; i++) { var a = axes[i]; a.innerHTML = i + ": " + controller.axes[i].toFixed(4); a.setAttribute("value", controller.axes[i]); } } rAF(updateStatus); } function scanGamepads() { var gamepads = navigator.getGamepads ? navigator.getGamepads() : []; for (var i = 0; i < gamepads.length; i++) { if (gamepads[i]) { if (!(gamepads[i].index in controllers)) { addGamepad(gamepads[i]); console.log("a"); } else { controllers[gamepads[i].index] = gamepads[i]; //console.log("b"); } } } } if (haveEvents) { window.addEventListener("gamepadconnected", connectHandler); window.addEventListener("gamepaddisconnected", disconnectHandler); } else { setInterval(scanGamepads, 500); }
実行させてみる
GithubのリポジトリをWeb公開状態に以下においてありますのでそのまま動作させてみてください。
https://ueponx.github.io/gamepadapi/
2つのファイル(index.html
、gamepad.js
)をローカルPCの同じディレクトリにおいても同じ様に使用できます。
index.html
の開くと以下のように表示されますので、
Gamepad
を接続するか、Gamepad
のいずれかのボタンを押すと表示が変更されます。
今回接続したのは以下のUSB接続をゲームパッドを接続していますが、ちゃんと認識してくれているようです。
ボタンは物理的には10あるのですが、認識されているのは連射機能とそのOFFを除く8つのボタンでした。 また、方向ボタン(十字ボタン)はアナログスティックとして認識されているようです。ただ動かすとわかるのですが、デジタル的な動きをしているようです。
また複数のゲームパッドを接続しても認識してくれます。
2つ目に接続したのはXBOX Oneの無線コントローラになります。
詳細は以下の通りです。
おわりに
Gamepad API
を使ってみました。まだDraft状態のAPIではありますが、現行のモダンブラウザであれば比較的対応しているのでわりといい感じです。
ネットの情報ではブラウザ毎の挙動が微妙に違っているという話もありますが…。そのうち正式な対応されるでしょう。
対応状況をみると、IE
は兎も角としてSafari
…
あと、パッドのおすすめはXBOXなコントローラーをデフォルト扱いとして開発されているようなので、今後コントローラーを購入するのであればXBOX用のものを おすすめします。
できれば、現在あるWebアプリなどにJoy2Keyみたいな感じでこの機能を簡単でアタッチする方法とかがあるといいなあとか思いました。 なにかつかえますかねえ。IotデバイスでWebインターフェースを作るようなパターンであれば行けそうな気もしますが。
今回やってて痛感しましたが、Javascriptのスクリプト書くのは問題ないんですけど、HTMLを書くのがもう面倒で厳しいかも。JQueryとかBootstrapにシフトして行きたいです…その他のフレームワークに行ってもいいのかも。