1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
|
---
title: ゲームパッド API の使用
slug: Web/Guide/API/Gamepad
tags:
- API
- Advanced
- Gamepad API
- Games
- Guide
translation_of: Web/API/Gamepad_API/Using_the_Gamepad_API
---
<p>{{DefaultAPISidebar("Gamepad API")}}</p>
<p><span class="seoSummary">HTML5 はリッチでインタラクティブなゲームを開発するために必要なコンポーネントを多く搭載しています。 <code><canvas></code> や WebGL、 <code><audio></code> に <code><video></code> などの技術は、今までネイティブコードを書く必要のあった機能をサポートできるほどに成長しました。ゲームパッド API は開発者とデザイナーにゲームパッドやコントローラーへのアクセスを提供するものです。</span></p>
<p><a href="/ja/docs/Web/API/Gamepad_API">ゲームパッドAPI</a> は {{ domxref("Window") }} オブジェクトにゲームパッドとコントローラー (以下、ゲームパッド) の状態を読み取る新しいイベントをいくつか追加します。さらに、 {{ domxref("Gamepad") }} というゲームパッドの接続状態が得られるオブジェクトと {{ domxref("navigator.getGamepads") }} というゲームパッドの一覧を取得できるメソッドが追加されます。</p>
<h2 id="conntecting" name="conntecting">ゲームパッドの接続</h2>
<p>新しいゲームパッドが接続された時、アクティブなページは {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }} イベントを受け取ります。ページ読み込み時にゲームパッドがすでに接続されている場合、ゲームパッドのボタンを押すなどの操作をした時に {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }} イベントがアクティブなページに対して発生します。</p>
<div class="blockIndicator geckoVersionNote">
<p>Firefox では、ページが見える状態でかつユーザーによるゲームパッドの操作を受け付けたときにのみ、ゲームパッドが利用可能になります。これによって、ユーザーを特定する Fingerprinting に利用されることを防止しています。一度一つのコントローラーが操作されれば、他のコントローラーも自動で接続され利用可能になります。</p>
</div>
<p>以下のようにして {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }} を使用します:</p>
<pre class="brush: js; notranslate">window.addEventListener("gamepadconnected", function(e) {
console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
e.gamepad.index, e.gamepad.id,
e.gamepad.buttons.length, e.gamepad.axes.length);
});
</pre>
<p>ゲームパッドはそれぞれ固有の ID を <code>gamepad</code> プロパティの中に持っています。</p>
<h2 id="disconnecting" name="disconnecting">ゲームパッドの切断</h2>
<p>ゲームパッドが切断されると、ゲームパッドが以前に受信したデータ(例: {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }} )があると、2番目のイベント(例: {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }} )がフォーカスされたウィンドウにディスパッチします:</p>
<pre class="brush: js notranslate">window.addEventListener("gamepaddisconnected", function(e) {
console.log("Gamepad disconnected from index %d: %s",
e.gamepad.index, e.gamepad.id);
});</pre>
<p>ゲームパッドの {{domxref("Gamepad.index", "index")}} というプロパティは同じタイプの複数のコントローラーが使用されている場合であっても、システムに接続されたデバイスごとにユニークになります。 <code>Index</code> プロパティもまた {{ domxref("Navigator.getGamepads()") }} として戻される {{jsxref("Array")}} の index として機能します。</p>
<pre class="brush: js notranslate">var gamepads = {};
function gamepadHandler(event, connecting) {
var gamepad = event.gamepad;
// Note:
// gamepad === navigator.getGamepads()[gamepad.index]
if (connecting) {
gamepads[gamepad.index] = gamepad;
} else {
delete gamepads[gamepad.index];
}
}
window.addEventListener("gamepadconnected", function(e) { gamepadHandler(e, true); }, false);
window.addEventListener("gamepaddisconnected", function(e) { gamepadHandler(e, false); }, false);
</pre>
<p>この前の例ではイベントが完了した後に <code>gamepad</code> プロパティがどのように保持できるかを示しています - 後でデバイスの状態照会のために使用する技術となります。</p>
<h2 id="querying" name="querying">Gamepad オブジェクトの問い合わせ</h2>
<p>ご覧のように、上述の <strong>gamepad</strong> イベントは {{ domxref("Gamepad") }} オブジェクトを返すイベントオブジェクト、上の <code>gamepad</code> のプロパティが含まれています。複数のゲームパッド(すなわち、そのID ) を一度に接続される可能性があるため、イベントを発生させたのはどのゲームパッドを決定するためにこれらを使用することができます。それへの参照を保持し、それがボタンや軸のいずれかの時点で押されているかを知るために照会するなど、{{ domxref("Gamepad") }} オブジェクトから様々なことを行うことができます。そうすることで、多くの場合、今回と次回のイベント発生とゲームパッドの状態を知っておく必要があり、ゲームやその他のインタラクティブな Web ページであることが望ましいです。</p>
<p>このようなチェックを実行すると、開発者はゲームパッドやゲームパッドの状態に基づいて、現在のフレームのための意思決定を行うために必要なアニメーションループ (例 : {{ domxref("Window.requestAnimationFrame","requestAnimationFrame") }}) と一緒に {{ domxref("Gamepad") }} オブジェクトを使用して関与する傾向があります。</p>
<p>{{ domxref("Navigator.getGamepads()") }} メソッドは現在 Web ページから見える {{ domxref("Gamepad") }} オブジェクト (ゲームパッドが繋がっていない時は毎回 null が返される ) のような、すべてのデバイスを配列として戻します。これは、同じ情報を得るために使用することができます。例えば、 以下に示すように上記の最初のコード例を書き換えます。</p>
<pre class="brush: js notranslate">window.addEventListener("gamepadconnected", function(e) {
var gp = navigator.getGamepads()[e.gamepad.index];
console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
gp.index, gp.id,
gp.buttons.length, gp.axes.length);
});</pre>
<p>{{ domxref("Gamepad") }} オブジェクトの機能は以下の通りです。</p>
<ul>
<li><code>id</code>: コントローラーに関する情報を含んだ文字列です。これは厳密には指定されていなく、Firefox では、コントローラーのUSBベンダと製品IDを含んでいる2つの4桁16進数字列、およびドライバーから提供されるようなコントローラーの名前といった3つの情報が、ダッシュ (<code>-</code>) により分離され含まれています。この情報はユーザーへの有益なフィードバックを表示するとともに、デバイスのコントロールのマッピングを見つけることができるようにします。</li>
<li><code>index</code>: 現在システムに接続されているゲームパッドごとに固有の整数。この値は複数のコントローラーを区別するために使用できます。デバイスを切断してから新しいデバイスを接続すると、以前のインデックスが再利用されることに注意してください。</li>
<li><code>mapping</code>: ブラウザがデバイス上のコントロールを既知のレイアウトに再マップしたかどうかを示す文字列。現在、サポートされている既知のレイアウト - <a href="https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#remapping">標準のゲームパッド</a>は1つしかありません。ブラウザがデバイス上のコントロールをそのレイアウトにマッピングできる場合、 <code>mapping</code> プロパティは文字列 <code>standard</code> に設定されます。</li>
<li><code>connected</code>:ゲームパッドがシステムに接続されているかどうかを示すブール値。もし接続されている場合は <code>True</code>。接続されていない場合は <code>False</code> が設定されます。</li>
<li><code>buttons</code>: デバイス上に存在するボタンを表す {{ domxref("GamepadButton") }} オブジェクトの配列。各 {{ domxref("GamepadButton") }} には、<code>pressed</code> プロパティと <code>value</code> のプロパティがあります。
<ul>
<li><code>pressed</code> プロパティは、ボタンが現在押されている (<code>true</code>) か押されていない (<code>false</code>) かを示すブール値です。></li>
<li><code>value</code> プロパティは、現代の多くのゲームパッドのトリガなど、アナログボタンの表示を有効にするために使用される浮動小数点値です。値は 0.0..1.0 の範囲に正規化され、 0.0 は押されていないボタンを表し、 1.0 は完全に押されたボタンを表します。</li>
</ul>
</li>
<li><code>axes</code>: デバイス上に軸があるコントロールを表す配列 (例:アナログサムスティック)。 配列の各エントリは-1.0〜1.0の範囲の浮動小数点値で、最小値 (-1.0) から最大値 (1.0) までの軸の位置を表します。</li>
<li><code>timestamp</code>: このゲームパッドのデータが最後に更新された時刻を表す {{ domxref("DOMHighResTimeStamp") }} を返します。この値により、開発者は <code>axes</code> と <code>button</code> のデータがハードウェアから更新されたかどうかを判断できます。 値は、 {{ domxref("PerformanceTiming") }} インターフェースの <code>navigationStart</code> 属性との相対値でなければなりません。 値は単調に増加します。つまり、更新の順序を決定するために比較することができます。新しい値は常に古い値よりも大きいか等しいためです。このプロパティは現在 Firefox ではサポートされていません。</li>
</ul>
<div class="note">
<p><strong>注</strong>: Gamepadオブジェクトは、セキュリティ上の理由から {{ domxref("Window") }} オブジェクトではなく {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }} イベントで使用できます。一度リファレンスを取得すると、そのプロパティでゲームパッドの現在の状態に関する情報を照会できます。 このオブジェクトは、ゲームパッドの状態が変わるたびに更新されます。</p>
</div>
<h3 id="Using_button_information" name="Using_button_information">ボタン情報の使用</h3>
<p>1つのゲームパッドの接続情報を表示する簡単な例を見てみましょう (後続のゲームパッド接続を無視します)。ゲームパッドの右側にある4つのゲームパッドボタンを使用してボールを画面の周りに移動できます。<a href="http://chrisdavidmills.github.io/gamepad-buttons/">デモをライブで見ることができ</a>ことができ、Githubで<a href="https://github.com/chrisdavidmills/gamepad-buttons/tree/master">ソースコードを見つけることができます</a>。</p>
<p>まず、いくつかの変数を宣言します: 接続情報が書き込まれる <code>gamepadInfo</code> のパラグラフ、移動する <code>ball</code>、<code>requestAnimation Frame</code> の ID として機能する <code>start</code> 変数、ボールを移動するための位置変更子として機能する a および b 変数、および短縮形変数 これは、 {{ domxref("Window.requestAnimationFrame", "requestAnimationFrame()") }} および {{ domxref("Window.cancelAnimationFrame", "cancelAnimationFrame()") }} クロスブラウザフォークで使用されます。</p>
<pre class="brush: js notranslate">var gamepadInfo = document.getElementById("gamepad-info");
var ball = document.getElementById("ball");
var start;
var a = 0;
var b = 0;
</pre>
<p>次に {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }} イベントを使用して、接続されているゲームパッドを確認します。接続されると {{ domxref("Navigator.getGamepads()") }}<code>[0]</code> を使用してゲームパッドを取得し、ゲームパッドに関する情報をゲームパッドの情報 <code>div</code> に出力し、全体のボールの動きが始まる <code>gameLoop()</code> 関数が呼び出されます。</p>
<pre class="brush: js notranslate">window.addEventListener("gamepadconnected", function(e) {
var gp = navigator.getGamepads()[e.gamepad.index];
gamepadInfo.innerHTML = "Gamepad connected at index " + gp.index + ": " + gp.id + ". It has " + gp.buttons.length + " buttons and " + gp.axes.length + " axes.";
gameLoop();
});</pre>
<p>これで {{domxref("Window/gamepaddisconnected_event", "gamepaddisconnected")}} イベントを使用して、ゲームパッドが再び切断されたかどうかを確認します。 もしそうならば、 {{DOMxRef("Window.requestAnimationFrame", "requestAnimationFrame()")}} ループ (下記参照) を停止し、ゲームパッドの情報を元の状態に戻します。</p>
<pre class="brush: js notranslate">window.addEventListener("gamepaddisconnected", function(e) {
gamepadInfo.innerHTML = "Waiting for gamepad.";
cancelRequestAnimationFrame(start);
});</pre>
<p>Chrome では異なる挙動になります。変数にゲームパッドの最新の状態を常に保存するのではなく、スナップショットを保存するだけなので、 Chrome で同じことを行うにはポーリングしてから {{ domxref("Gamepad") }} オブジェクトをコードで使用する必要があり、それは利用可能です。私たちはこれを {{ domxref("Window.setInterval()") }} オブジェクトが利用可能になると、ゲームパッド情報が出力され、ゲームループが開始され、 {{ domxref("Window.clearInterval()") }} を使用して間隔がクリアされます。 Chrome {{ domxref("Navigator.getGamepads()") }} の古いバージョンでは、 Webkit 接頭辞を使用して実装されています。下位互換性のために、接頭辞付きのバージョンと関数の標準バージョンの両方を検出して処理しようとします。</p>
<pre class="brush: js notranslate">var interval;
if (!('ongamepadconnected' in window)) {
// No gamepad events available, poll instead.
interval = setInterval(pollGamepads, 500);
}
function pollGamepads() {
var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
for (var i = 0; i < gamepads.length; i++) {
var gp = gamepads[i];
if (gp) {
gamepadInfo.innerHTML = "Gamepad connected at index " + gp.index + ": " + gp.id +
". It has " + gp.buttons.length + " buttons and " + gp.axes.length + " axes.";
gameLoop();
clearInterval(interval);
}
}
}</pre>
<p>今度はメインのゲームループです。ループが実行されるたびに、4つのボタンの1つが押されているかどうかがチェックされます。そうすると、<code>a</code> と <code>b</code> の移動変数の値を適切に更新し、 {{ cssxref("left") }} と {{ cssxref("top") }} のプロパティを更新し、その値を <code>a</code> および <code>b</code> とする。これはボールを画面の周りに動かす効果があります。 現在のバージョンの Chrome (この記事の執筆時点ではバージョン34) では、ボタンの値は {{ domxref("GamepadButton") }} オブジェクトではなく、 double 値の配列として保存されます。</p>
<p>この作業がすべて完了したら、 <code>requestAnimationFrame()</code> を使用して <code>gameLoop()</code> を再び実行して次のアニメーションフレームを要求します。</p>
<pre class="brush: js notranslate">function buttonPressed(b) {
if (typeof(b) == "object") {
return b.pressed;
}
return b == 1.0;
}
function gameLoop() {
var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
if (!gamepads) {
return;
}
var gp = gamepads[0];
if (buttonPressed(gp.buttons[0])) {
b--;
} else if (buttonPressed(gp.buttons[2])) {
b++;
}
if (buttonPressed(gp.buttons[1])) {
a++;
} else if (buttonPressed(gp.buttons[3])) {
a--;
}
ball.style.left = a * 2 + "px";
ball.style.top = b * 2 + "px";
start = requestAnimationFrame(gameLoop);
}</pre>
<h2 id="Complete_example_Displaying_gamepad_state" name="Complete_example_Displaying_gamepad_state">完全な例: ゲームパッドの状態を表示する</h2>
<p>この例では、 {{ domxref("Gamepad") }} オブジェクト、 {{ domxref("Window/gamepadconnected_event", "gamepadconnected") }} イベント、 {{domxref("Window/gamepaddisconnected_event", "gamepaddisconnected")}} イベントを使用してシステムに接続されているすべてのゲームパッドの状態を表示します。<a href="http://luser.github.io/gamepadtest/">デモを見て</a>、Githubの<a href="https://github.com/luser/gamepadtest">完全なソースコード</a>を見ることができます。</p>
<pre class="brush: js notranslate">var haveEvents = 'ongamepadconnected' in window;
var controllers = {};
function connecthandler(e) {
addgamepad(e.gamepad);
}
function addgamepad(gamepad) {
controllers[gamepad.index] = gamepad;
var d = document.createElement("div");
d.setAttribute("id", "controller" + gamepad.index);
var t = document.createElement("h1");
t.appendChild(document.createTextNode("gamepad: " + gamepad.id));
d.appendChild(t);
var b = document.createElement("div");
b.className = "buttons";
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);
var a = document.createElement("div");
a.className = "axes";
for (var i = 0; i < gamepad.axes.length; i++) {
var p = document.createElement("progress");
p.className = "axis";
//p.id = "a" + i;
p.setAttribute("max", "2");
p.setAttribute("value", "1");
p.innerHTML = i;
a.appendChild(p);
}
d.appendChild(a);
// See https://github.com/luser/gamepadtest/blob/master/index.html
var start = document.getElementById("start");
if (start) {
start.style.display = "none";
}
document.body.appendChild(d);
requestAnimationFrame(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() {
if (!haveEvents) {
scangamepads();
}
var i = 0;
var j;
for (j in controllers) {
var controller = controllers[j];
var d = document.getElementById("controller" + j);
var buttons = d.getElementsByClassName("button");
for (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 (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] + 1);
}
}
requestAnimationFrame(updateStatus);
}
function scangamepads() {
var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
for (var i = 0; i < gamepads.length; i++) {
if (gamepads[i]) {
if (gamepads[i].index in controllers) {
controllers[gamepads[i].index] = gamepads[i];
} else {
addgamepad(gamepads[i]);
}
}
}
}
window.addEventListener("gamepadconnected", connecthandler);
window.addEventListener("gamepaddisconnected", disconnecthandler);
if (!haveEvents) {
setInterval(scangamepads, 500);
}</pre>
<h2 id="Specifications" name="Specifications">仕様書</h2>
<table class="standard-table">
<thead>
<tr>
<th scope="col">仕様書</th>
<th scope="col">状態</th>
<th scope="col">備考</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{SpecName("Gamepad", "#gamepad-interface", "Gamepad")}}</td>
<td>{{Spec2("Gamepad")}}</td>
<td>初回定義</td>
</tr>
</tbody>
</table>
<h2 id="Browser_compatibility" name="Browser_compatibility">ブラウザーの互換性</h2>
<div class="hidden">
<div class="hidden">このページの互換性一覧表は構造化データから生成されています。データに協力していただけるのであれば、 <a class="external" href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a> をチェックアウトしてプルリクエストを送信してください。</div>
</div>
<p>{{Compat("api.Gamepad")}}</p>
|