aboutsummaryrefslogtreecommitdiff
path: root/files/ja/games/techniques/controls_gamepad_api/index.html
blob: adb633b4cd02cb56340fa4ec9396c9ff33b28324 (plain)
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
---
title: Gamepad APIを使用したコントロールの実装
slug: Games/Techniques/Controls_Gamepad_API
translation_of: Games/Techniques/Controls_Gamepad_API
---
<div>{{GamesSidebar}}</div><p class="summary"><span id="result_box" lang="ja"><span>この記事では、Gamepad API を使用して Web ゲーム用の効果的なクロスブラウザー制御システムを実装し、コンソールゲームコントローラーを使用してWebゲームを制御できるようにします。</span></span><a href="http://enclavegames.com/">Enclave Games</a><span lang="ja"><span> によって作成されたケーススタディゲーム Hungry Fridge を掲載しています。</span></span></p>

<h2 id="Web_ゲームのコントロール">Web ゲームのコントロール</h2>

<p><span id="result_box" lang="ja"><span>歴史的には、テレビに接続されたコンソールでゲームをすること、PC 上でのゲームとはまったく異なる体験でした。</span></span><span id="result_box" lang="ja"><span>最終的にはドライバとプラグインにより、コンソールゲームパッドをデスクトップゲームーネイティブかブラウザで実行されるゲームー</span></span><span lang="ja"><span>で使用できるようになりました。</span> <span>HTML5 の時代になり、</span></span><a href="/ja/docs/Web/API/Gamepad_API">Gamepad API</a><span lang="ja"><span> が完成し、プラグインなしでゲームパッドコントローラを使用してブラウザベースのゲームをプレイできるようになりました。</span> <span>Gamepad API は、入力を処理するために JavaScript コード内で使用できるボタンの押下と軸の変更を公開するインターフェイスを提供することで、これを実現します。</span> <span>これらは、ブラウザゲームにとって良いことです。</span></span></p>

<p><img alt="gamepad-controls" src="http://end3r.com/tmp/gamepad/gamepadapi-hungryfridge-img01.png" style="display: block; height: 400px; margin: 0px auto; width: 600px;"></p>

<h2 id="API_ステータスとブラウザサポート">API ステータスとブラウザサポート</h2>

<p><a href="http://www.w3.org/TR/gamepad/">Gamepad API</a> は W3C のプロセスではまだドラフトの状態であり、まだ実装が変わることがありますが、<a href="http://caniuse.com/gamepad">ブラウザサポート</a>はかなり良い状態です。Firefox 29 以降はそのまま使用できます。Opera はバージョン 22 以降で API をサポートしています(<span id="result_box" lang="ja"><span>ChromeのBlinkエンジンを使用していることを考えると驚くことではありません</span></span>)。Microsoft はEdge にて API をサポートしており、Gamepad API は4つの中心的なブラウザーにを現在サポートされています。</p>

<h2 id="どのゲームパッドが良いか?">どのゲームパッドが良いか?</h2>

<p><img alt="gamepad devices" src="http://end3r.com/tmp/gamepad/devices.jpg" style="display: block; height: 450px; margin: 0px auto; width: 600px;"></p>

<p>現在最も人気のあるゲームパッドは XBox 360、XBox One、PS3 や PS4 であり、これらは かなりテストが行われており、Windows や Mac OS X のブラウザで Gamepad API がうまく動いています。</p>

<p><span id="result_box" lang="ja"><span>さまざまなボタンレイアウトを備えたデバイスも多数あり、ブラウザの実装によって多かれ少なかれ動作します。</span> <span>この記事で取り上げたコードはいくつかのゲームパッドでテストしましたが、著者の好みはワイヤレス XBox 360 コントローラと Mac OS X 上の Firefox ブラウザの組み合わせです。</span></span></p>

<h2 id="ケーススタディ_Hungry_Fridge">ケーススタディ: Hungry Fridge</h2>

<p><a href="https://github.com/blog/1674-github-game-off-ii">GitHub Game Off II</a><span lang="ja"><span> のコンペが2013年11月に実施され、</span></span><a href="http://enclavegames.com/">Enclave Games</a><span lang="ja"><span> が参加することに決めました。</span> <span>競争のテーマは「変化」だったため、彼らは健康食品(リンゴ、ニンジン、レタス)をタップして飢えた冷蔵庫に食べさせ、「悪い」食べ物(ビール、ハンバーガー、ピザ)を避けなければならないゲームを提出しました。</span> <span>カウントダウンは、冷蔵庫が数秒ごとに食べたい食べ物のタイプを変えるので、注意して素早く行動する必要があります。</span> <span>あなたは</span></span><a href="http://enclavegames.com/games/hungry-fridge/"> Hungry Fridge</a><span lang="ja"><span></span></span><a href="http://enclavegames.com/games/hungry-fridge/">ここ</a><span lang="ja"><span>で試すことができます。</span></span></p>

<p><img alt="hungryfridge-mainmenu" src="http://end3r.com/tmp/gamepad/gamepadapi-hungryfridge-img02.png" style="display: block; height: 333px; margin: 0px auto; width: 500px;"></p>

<p><span id="result_box" lang="ja"><span>2番目の、隠された「変更」の実装は、静的な冷蔵庫を本格的な移動式射撃食堂に変えることです。</span> <span>コントローラーを接続すると、ゲームが大きく変わります(Hungry FridgeがSuper Turbo Hungry Fridgeに変わります)、Gamepad API を使用して装甲冷蔵庫を制御できます。</span> <span>あなたは食べ物を撃たなければなりませんが、もう一度、冷蔵庫が各所で食べたい食べ物の種類を見つけなければなりません。そうしないとエネルギーが失われます。</span></span></p>

<p><img alt="hungryfridge-howtoplay-gamepad" src="http://end3r.com/tmp/gamepad/gamepadapi-hungryfridge-img03.png" style="display: block; height: 333px; margin: 0px auto; width: 500px;"></p>

<p><span class="short_text" id="result_box" lang="ja"><span>このゲームは、2つの全く異なるタイプの「変化」をカプセル化しています。</span></span></p>

<p><img alt="hungryfridge-gameplay-gamepad" src="http://end3r.com/tmp/gamepad/gamepadapi-hungryfridge-img04.png" style="display: block; height: 333px; margin: 0px auto; width: 500px;"></p>

<h2 id="デモ">デモ</h2>

<p><span id="result_box" lang="ja"><span>Hungry Fridge ゲームのフルバージョンが最初に構築され、次に Gamepad API が実際に表示され、JavaScript のソースコードが表示され、</span></span><a href="https://end3r.github.io/Gamepad-API-Content-Kit/demo/demo.html">簡単なデモ</a><span lang="ja"><span>が作成されました。</span> <span>これはGitHubで利用可能な</span></span> <a href="https://end3r.github.io/Gamepad-API-Content-Kit/">Gamepad API Content Kit</a> <span lang="ja"><span>の一部であり、コードを深く掘り下げてどのように動作するかを正確に調べることができます。</span></span></p>

<p><img alt="Hungry Fridge demo using Gamepad API" src="http://end3r.com/tmp/gamepad/super-turbo.png" style="display: block; height: 333px; margin: 0px auto; width: 500px;"></p>

<p><span id="result_box" lang="ja"><span>以下に説明するコードは、Hungry Fridge ゲームのフルバージョンからのものですが、デモのものとほぼ同じです。唯一の違いは、フルバージョンでは、スーパーターボモードを使用してゲームを起動するかどうかを決定する</span></span><code>ターボ</code><span lang="ja"><span>変数</span><span></span> <span>これは独立して機能するので、ゲームパッドが接続されていなくてもオンにすることができます。</span></span></p>

<div class="note">
<p><strong>注記</strong>: <span id="result_box" lang="ja"><span>イースターエッグタイム:ゲームパッドを接続せずにデスクトップにSuper Turbo Hungry Fridgeを起動する隠しオプションがあります。スクリーンの右上にあるゲームパッドアイコンをクリックします。</span> <span>それはスーパーターボモードでゲームを起動し、あなたはキーボードで冷蔵庫を制御することができます:タレットを左右に回すためのAとD、撮影のためのW、動きのための矢印キー。</span></span></p>
</div>

<h2 id="実装">実装</h2>

<p><span id="result_box" lang="ja"><span>Gamepad API には</span></span><code> gamepadconnected</code><span lang="ja"><span> と gamepaddisconnected という2つの重要なイベントがあります。</span> <span>最初のゲームはゲームパッドが切断されたとき(ユーザーが物理的に、または非アクティブのため)ゲームパッドが新しいゲームパッドの接続を検出したときに起動されます。デモでは、</span></span><code>gamepadAPI </code><span lang="ja"><span>オブジェクトは</span></span><span class="short_text" id="result_box" lang="ja"><span>、APIに関連するすべてを格納するために使用されます:</span></span></p>

<pre class="brush: js"><code>var gamepadAPI = {
  controller: {},
  turbo: false,
  connect: function() {},
  disconnect: function() {},
  update: function() {},
  buttonPressed: function() {},
  buttons: [],
  buttonsCache: [],
  buttonsStatus: [],
  axesStatus: []
};</code></pre>

<p><code>buttons</code> の配列は XBox 360 ボタンレイアウトを格納します:</p>

<pre class="brush: js"><code>buttons: [
  'DPad-Up','DPad-Down','DPad-Left','DPad-Right',
  'Start','Back','Axis-Left','Axis-Right',
  'LB','RB','Power','A','B','X','Y',
],</code></pre>

<p><span id="result_box" lang="ja"><span>これは、PS3コントローラ(または名前のない、一般的なもの)のような他のタイプのゲームパッドでは異なる場合があり、期待しているボタンが実際に得るのと同じボタンになるか注意する必要があり、また仮定してはいけません:</span></span></p>

<pre class="brush: js"><code>window.addEventListener("gamepadconnected", gamepadAPI.connect);
window.addEventListener("gamepaddisconnected", gamepadAPI.disconnect);</code></pre>

<p><span id="result_box" lang="ja"><span>セキュリティポリシーのため、イベントが発生するとページが表示されている間にコントローラーと最初にやりとりする必要があります。</span> <span>APIがユーザーとのやりとりなしで動作した場合、APIを認識することなくフィンガープリントに使用できます。</span></span></p>

<p><span class="short_text" id="result_box" lang="ja"><span>どちらの関数もかなりシンプルです:</span></span></p>

<pre class="brush: js"><code>connect: function(evt) {
  gamepadAPI.controller = evt.gamepad;
  gamepadAPI.turbo = true;
  console.log('Gamepad connected.');
},</code></pre>

<p><code>connect()</code> <span id="result_box" lang="ja"><span>関数はイベントをパラメータとして受け取り、</span></span><code>gamepad </code><span lang="ja"><span>オブジェクトを </span></span><code>gamepadAPI.controller</code><span lang="ja"><span> 変数に割り当てます。</span></span><span class="short_text" id="result_box" lang="ja"><span>このゲームでは1つのゲームパッドしか使用しないため、ゲームパッドの配列ではなく単一のオブジェクトです。</span></span><span id="result_box" lang="ja"><span>次に、</span></span><code>turbo </code><span lang="ja"><span>プロパティを </span></span><code>true </code><span lang="ja"><span>に設定します。</span><span>(この目的のために </span></span><code>gamepad.connected </code><span lang="ja"><span>というブール値を使用することができましたが、上で説明した理由から、ゲームパッドを接続しなくてもターボモードを有効にするための別の変数が必要でした)。</span></span></p>

<pre class="brush: js"><code>disconnect: function(evt) {
  gamepadAPI.turbo = false;
  delete gamepadAPI.controller;
  console.log('Gamepad disconnected.');
},</code></pre>

<p><code>disconnect</code> 関数は <code>gamepad.turbo プロパティを</code> <code>false</code> に設定し、gamepad オブジェクトを含む変数を削除します。</p>

<h3 id="Gamepad_オブジェクト">Gamepad オブジェクト</h3>

<p><code>gamepad </code><span class="short_text" lang="ja"><span>オブジェクトには、ボタンや軸の状態が最も重要な情報がたくさんあります</span></span></p>

<ul>
 <li><code>id</code>: コントローラーに関する情報を含む文字列。</li>
 <li><code>index</code>: 接続したデバイスを定義するユニークな識別子。</li>
 <li><code>connected</code>: 接続時に <code>true</code> になる真偽値の変数。</li>
 <li><code>mapping</code>: <span class="short_text" id="result_box" lang="ja"><span>ボタンのレイアウトタイプ。</span> <span>標準は現在利用可能な唯一のオプション。</span></span></li>
 <li><code>axes</code>: <span class="short_text" id="result_box" lang="ja"><span>各軸の状態。浮動小数点値の配列で表される。</span></span></li>
 <li><code>buttons</code> : <code>pressed </code><span lang="ja"><span>プロパティと </span></span><code>value</code><span lang="ja"><span> のプロパティを含む </span></span><code>GamepadButton</code><span lang="ja"><span> オブジェクトの配列で表される各ボタンの状態。</span></span></li>
</ul>

<p><code>index </code><span lang="ja"><span>変数は、2つ以上のコントローラを接続しており、2つのデバイスが接続されている2人のゲームがある場合など、それらを識別する必要がある場合に便利です。</span></span></p>

<h3 id="gamepad_オブジェクトのクエリ">gamepad オブジェクトのクエリ</h3>

<p><code>connect() </code><span lang="ja"><span>と </span></span><code>disconnect()</code><span lang="ja"><span> のほかに、</span></span><code>gamepadAPI </code><span lang="ja"><span>オブジェクトには</span></span><code> update() </code><span lang="ja"><span>と </span></span><code>buttonPressed()</code><span lang="ja"><span> の2つのメソッドがあります。</span> </span><code>update() </code><span lang="ja"><span>は、ゲームループ内のすべてのフレームで実行され、ゲームパッドオブジェクトの実際のステータスを定期的に更新します:</span></span></p>

<pre class="brush: js"><code>update: function() {
  // clear the buttons cache
  gamepadAPI.buttonsCache = [];
  // move the buttons status from the previous frame to the cache
  for(var k=0; k&lt;gamepadAPI.buttonsStatus.length; k++) {
    gamepadAPI.buttonsCache[k] = gamepadAPI.buttonsStatus[k];
  }
  // clear the buttons status
  gamepadAPI.buttonsStatus = [];
  // get the gamepad object
  var c = gamepadAPI.controller || {};

  // loop through buttons and push the pressed ones to the array
  var pressed = [];
  if(c.buttons) {
    for(var b=0,t=c.buttons.length; b&lt;t; b++) {
      if(c.buttons[b].pressed) {
        pressed.push(gamepadAPI.buttons[b]);
      }
    }
  }
  // loop through axes and push their values to the array
  var axes = [];
  if(c.axes) {
    for(var a=0,x=c.axes.length; a&lt;x; a++) {
      axes.push(c.axes[a].toFixed(2));
    }
  }
  // assign received values
  gamepadAPI.axesStatus = axes;
  gamepadAPI.buttonsStatus = pressed;
  // return buttons for debugging purposes
  return pressed;
},</code></pre>

<p><span id="result_box" lang="ja"><span>すべてのフレームで、</span></span><code>update() </code><span lang="ja"><span>は前のフレームで押されたボタンを </span></span><code>buttonsCache</code><span lang="ja"><span> 配列に保存し、新しいものを </span></span><code>gamepadAPI.controller</code><span lang="ja"><span> オブジェクトから取得します。</span> <span>次に、実際の状態と値を取得するためにボタンと軸をループします。</span></span></p>

<h3 id="ボタンプレスの検出"><span class="short_text" id="result_box" lang="ja"><span>ボタンプレスの検出</span></span></h3>

<p><code>buttonPressed()</code>  <span lang="ja"><span>メソッドはメインのゲームループに配置され、ボタンの押下を待機します。</span> <span>2つのパラメータ、つまり聴きたいボタンと、ボタンを押したままにすることをゲームに伝える(オプション)方法があります。</span> <span>それがなければ、ボタンを放してもう一度押して、希望する効果を持たなければなりません。</span></span></p>

<pre class="brush: js"><code>buttonPressed: function(button, hold) {
  var newPress = false;
  // loop through pressed buttons
  for(var i=0,s=gamepadAPI.buttonsStatus.length; i&lt;s; i++) {
    // if we found the button we're looking for...
    if(gamepadAPI.buttonsStatus[i] == button) {
      // set the boolean variable to true
      newPress = true;
      // if we want to check the single press
      if(!hold) {
        // loop through the cached states from the previous frame
        for(var j=0,p=gamepadAPI.buttonsCache.length; j&lt;p; j++) {
          // if the button was already pressed, ignore new press
          if(gamepadAPI.buttonsCache[j] == button) {
            newPress = false;
          }
        }
      }
    }
  }
  return newPress;
},</code></pre>

<p><span id="result_box" lang="ja"><span>ボタンには、1回の押下と1回の押下の2種類のアクションがあります。</span> </span><code>newPress </code><span lang="ja"><span>ブール変数は、ボタンの新規押下があるかどうかを示します。</span> <span>次に、押されたボタンの配列をループします。指定されたボタンが探しているボタンと同じ場合、</span></span><code>newPress </code><span lang="ja"><span>変数は </span></span><code>true</code><span lang="ja"><span> に設定されます。</span> <span>プレスが新しいものかどうかを確認するために、プレイヤーがキーを保持していないので、ゲームループの前のフレームからのボタンのキャッシュ状態をループします。</span> <span>ボタンが見つかった場合、ボタンが押されていることを意味するので、新しいプレスはありません。</span> <span>最後に、</span></span><code>newPress </code><span lang="ja"><span>変数が返されます。</span> </span><code>buttonPressed</code><span lang="ja"> <span>関数は、このようなゲームの更新ループで使用されます:</span></span></p>

<pre class="brush: js"><code>if(gamepadAPI.turbo) {
  if(gamepadAPI.buttonPressed('A','hold')) {
    this.turbo_fire();
  }
  if(gamepadAPI.buttonPressed('B')) {
    this.managePause();
  }
}</code></pre>

<p><code>gamepadAPI.turbo </code><span lang="ja"><span></span></span><code>true </code><span lang="ja"><span>で、指定されたボタンが押された (または保持されている) 場合、それらに割り当てられた適切な関数を実行します。</span> <span>この場合、</span></span><code>A </code><span lang="ja"><span>を押すと、弾丸が発射され、</span></span><code>B </code><span lang="ja"><span>を押すとゲームが一時停止します。</span></span></p>

<h3 id="軸のしきい値"><span class="short_text" id="result_box" lang="ja"><span>軸のしきい値</span></span></h3>

<p><span id="result_box" lang="ja"><span>ボタンには </span></span><code>0 </code><span lang="ja"><span>または </span></span><code>1 </code><span lang="ja"><span>の二つの状態しかありませんが、アナログスティックは </span></span><code>X </code><span lang="ja"><span>軸と </span></span><code>Y </code><span lang="ja"><span>軸の両方に沿って </span></span><code>-1 </code><span lang="ja"><span></span></span><code>1 </code><span lang="ja"><span>の間の浮動小数点範囲を持っています。</span></span></p>

<p><img alt="axis threshold" src="http://end3r.com/tmp/gamepad/axis-threshold.png" style="display: block; height: 300px; margin: 0px auto; width: 400px;"></p>

<p><span id="result_box" lang="ja"><span>ゲームパッドは、ほこりのない場所からのゴミを得ることができます。つまり、正確な </span></span>-1 <span lang="ja"><span>または </span></span>1 <span lang="ja"><span>の値をチェックすることが問題になります。</span> <span>このため、軸の値のしきい値を設定すると効果的です。</span> 例えば<span></span></span><code>X </code><span lang="ja"><span>値が </span></span><code>0.5</code><span lang="ja"><span> より大きい場合にのみ、冷蔵庫のタンクが右に回転します:</span></span></p>

<pre><code>if(gamepadAPI.axesStatus[0].x &gt; 0.5) {
  this.player.angle += 3;
  this.turret.angle += 3;
}</code></pre>

<p><span id="result_box" lang="ja"><span>たとえ誤って少し動かしても、スティックが元の位置に戻らない場合でも、タンクが予期せず回転することはありません。</span></span></p>

<h2 id="仕様の更新"><span class="short_text" id="result_box" lang="ja"><span>仕様の更新</span></span></h2>

<p><span id="result_box" lang="ja"><span>1年以上の安定性の後、2015年4月に W3C Gamepad API 仕様が更新されました</span></span> (<a href="https://w3c.github.io/gamepad/">最新の仕様を参照</a>) <span lang="ja"><span>。それはあまり変わっていませんが、何が起こっているのかを知ることは良いことです。</span></span></p>

<h3 id="gamepads_の入手">gamepads の入手</h3>

<p>{{domxref("Naviagator.getGamepads()")}} <span id="result_box" lang="ja"><span>メソッドは、</span></span><a href="https://w3c.github.io/gamepad/#navigator-interface-extension">より長い解説とコード例</a><span lang="ja"><span>で更新されました。</span> <span>ここで、ゲームパッドの配列の長さは </span></span><code>n + 1</code><span lang="ja"><span> でなければなりません。ここで、</span></span><code>n </code><span lang="ja"><span>は接続されたデバイスの数です </span></span><span lang="ja"><span> 接続されたデバイスが1つでインデックスが1の場合、配列の長さは2で、</span> <span>[</span></span><code>null, </code><code>[object Gamepad]</code><code>] </code><span lang="ja"><span>をクリックします。</span> <span>デバイスが接続されていないか使用できない場合、デバイスの値は </span></span><code>null </code><span lang="ja"><span>に設定されます。</span></span></p>

<h3 id="マッピング標準"><span class="short_text" id="result_box" lang="ja"><span>マッピング標準</span></span></h3>

<p><span class="short_text" id="result_box" lang="ja"><span>マッピングタイプは、文字列ではなく列挙可能なオブジェクトになりました:</span></span></p>

<pre>enum GamepadMappingType {
    "",
    "standard"
};</pre>

<p><span id="result_box" lang="ja"><span>この列挙型は、ゲームパッドの既知のマッピングのセットを定義します。</span> <span>今のところ</span></span><code>標準</code><span lang="ja"><span>のレイアウトしかありませんが、将来は新しいレイアウトが登場する可能性があります。</span> <span>レイアウトが不明な場合は、空の文字列に設定されます。</span></span></p>

<h3 id="イベント">イベント</h3>

<p><span id="result_box" lang="ja"><span>スペックには、</span></span><code>gamepaddisconnected </code><span lang="ja"><span>と </span></span><code>gamepaddisconnected</code><span lang="ja"><span> よりも多くのイベントがありましたが、あまり役に立たないと思われていた仕様から削除されました。</span> <span>議論は、それを取り戻すべきかどうか、そしてどのような形で進行しているかについてはまだ進行中です。</span></span></p>

<h2 id="概要"><span class="short_text" id="result_box" lang="ja"><span>概要</span></span></h2>

<p><span id="result_box" lang="ja"><span>Gamepad APIは非常に開発が簡単です。</span> <span>プラグインを必要とせずに、コンソールに似たエクスペリエンスをブラウザに提供することがこれまで以上に簡単になりました。</span> </span><a href="http://enclavegames.com/games/hungry-fridge/">Hungry Fridge </a><span lang="ja"><span>ゲームのフルバージョンをブラウザで直接再生したり、</span></span><a href="https://marketplace.firefox.com/app/hungry-fridge">Firefox Marketplace</a> <span lang="ja"><span>からインストールしたり、</span></span><a href="https://github.com/EnclaveGames/Hungry-Fridge">Gamepad API Content Kit </a><span lang="ja"><span>のその他のリソースと一緒にデモのソースコードを確認したりすることができます。</span></span></p>