JavaScript ゲームにおける操作方法
[この記事は、Control mechanisms in JavaScript games の翻訳です。]
ラップトップやデスクトップをはじめ、スマートフォンやタブレット、TV や冷蔵庫でさえも、共通してブラウザを持つようになりました。ということは、それらの端末上で HTML5 ゲームをプレイできるようになったという風にも思えますが、実際にプレイするためには、ゲーム画面を描画するのと同時に、何らかの方法でゲームを操作できなければなりません。キーボードやマウスはもちろん、タッチ操作、ゲームパッド、TV リモコン、さらにはバナナに至るまで、それぞれのプラットフォームごとに適した選択肢はたくさん存在します(MDN では、control mechanisms に関する一連の記事でそれらのインターフェースを取り上げています)。
今回は、実際のプロジェクトにおける例として、Phaser を用いて作成された Captain Rogers: Battle at Andromeda のデモを使ってぞれぞれの操作方法の実装方法について紹介します。プラットフォームの違いによってゲームの操作性がどう変わっていくかをご確認ください。また、異なるプラットフォームに対応する際に別々にビルドする必要がないということもお見せします。ウェブのマルチプラットフォームという特性によって、コーディングに多くの労力を費やすことなく複数のデバイスに対応できることがお分かりいただけると思います。
また、GitHub には、小さいですが純粋な JavaScript によるデモがオープンソースとして公開されています。操作方法がどのように実装されているかを実際に確認できるので、ご自身のゲーム開発プロジェクトでもお使いいただけます。以下に、主な項目をハイライトでご紹介しますので、このままお読みいただいても良いですし、今すぐコーディングを開始しても良いでしょう。
モバイル タッチ
HTML5 ゲームではモバイルファースト開発が人気ですので、まずはモバイル タッチへの対応から見ていきましょう。
document.addEventListener("touchstart", touchHandler); document.addEventListener("touchmove", touchHandler); function touchHandler(e) { if(e.touches) { playerX = e.touches[0].pageX - canvas.offsetLeft - playerWidth / 2; playerY = e.touches[0].pageY - canvas.offsetTop - playerHeight / 2; } }
たった数行の JavaScript コードですが、これがゲーム内でタッチ操作を実現するために必要な最低限のコードです。これだけでモバイル端末上で機能します。最初の 2 行は、タッチ操作に対するイベントリスナーの設定です。それぞれ、画面をタッチしたとき、および、指で画面をなぞったときに対応します。関数内では、タッチ操作が行われたことを確認し、プレイヤーの座標を設定しています。これにより、ゲームの Canvas 上でプレイヤーの機体が正しい位置に描画されます。
上記はベースとなるコードですので、ここからマルチタッチやジェスチャーなど様々な発展形を実装することができます(むしろ、実装した方がいいでしょう)。ゲームの種類や、何をどのように操作するかということを考慮して、実装する操作方法を決めましょう。また、移動用の矢印や発射ボタンなど、特定のアクションを起こすためのボタンを画面上に設置することもできます。詳しくは、Mobile touch controls をご覧ください。
デスクトップ マウスとキーボード
移動用の矢印について触れましたが、モバイル端末向けに画面上に矢印を表示するのはもちろん、デスクトップ向けにそれらを実装することも可能です。ゲーム内のキャラクターを動かす方法としては、カーソルキーや WASD キーがよく使われます。例えば、以下のコードではカーソルキーを使用しています。
document.addEventListener("keydown", keyDownHandler); function keyDownHandler(e) { if(e.keyCode == 39) { rightPressed = true; } else if(e.keyCode == 37) { leftPressed = true; } if(e.keyCode == 40) { downPressed = true; } else if(e.keyCode == 38) { upPressed = true; } }
押されたキーを定常的に検出しその情報を保存するということですので、これは描画ループとして以下のように処理することが可能です。
function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); if(rightPressed) { playerX += 5; } else if(leftPressed) { playerX -= 5; } if(downPressed) { playerY += 5; } else if(upPressed) { playerY -= 5; } ctx.drawImage(img, playerX, playerY); requestAnimationFrame(draw); }
プレイヤーの位置 x
および y
を表す変数が調整され、機体画像が新たな位置に表示されます。
一方、デスクトップにおけるマウスは、コーディングにおいてモバイルのタッチと非常に似ています。タッチやクリックが起こった場所に関する情報を取得し、プレイヤーの位置を更新するだけです。
document.addEventListener("mousemove", mouseMoveHandler); function mouseMoveHandler(e) { playerX = e.pageX - canvas.offsetLeft - playerWidth / 2; playerY = e.pageY - canvas.offsetTop - playerHeight / 2; }
マウスポインターの位置が変化するたびに mousemove
イベントが検出されるので、プレイヤーの位置がマウスポインターの中央の位置に来るように調整します。こうして、デスクトップのキーボードとマウスの両方で操作可能なゲームを実現することができます。さらに詳しく知りたい方は、Desktop mouse and keyboard controls をご覧ください。
ゲームパッド
個人的に大好きな操作方法です。プレゼンテーションを行う際は、HTML5 ベースの自分のスライドを Gamepad APIを使って操作しているくらいです。ゲームパッドについては、すでに数回お話したこともありますし、ゲームもいくつか実装したこともあります。詳しくは、Gamepad API Content Kit に全て記載しています。 とにかく、コンピューター上でコンソール気分を体験できることは本当に素晴らしいと思いますし、それがウェブ技術によって実現されているということも素晴らしいと思います!ゲームパッドを使用すれば、Captain Rogers のプレイ体験がさらに向上しますし、単純に良い気分になります。
Gamepad API への対応はタッチやキーボードよりもやや複雑になりますが、それでも非常に簡単です。
window.addEventListener("gamepadconnected", gamepadHandler); function gamepadHandler(e) { controller = e.gamepad; } function gamepadUpdateHandler() { buttonsPressed = []; if(controller.buttons) { for(var b=0; b<controller.buttons.length; b++) { if(controller.buttons[i].pressed) { buttonsPressed.push(b); } } } } function gamepadButtonPressedHandler(button) { var press = false; for(var i=0; i<buttonsPressed.length; i++) { if(buttonsPressed[i] == button) { press = true; } } return press; }
ゲームパッドが接続されるとイベントが発生し、ゲームパッドに関するデータの参照が変数に保存されます。以降は、その変数を使用してゲームパッドに関するデータにアクセスします。更新が起こると、押されたボタンの配列が新たに生成されるため、常に最新の状態が保持されます。また、あるボタンが押されたかどうかを判定するために配列をループ処理する関数もあります。これらはキーボードチェックと同様に、描画ループの中で使用できます。
function draw() { // ... gamepadUpdateHandler(); if(gamepadButtonPressedHandler(0)) { playerY -= 5; } else if(gamepadButtonPressedHandler(1)) { playerY += 5; } if(gamepadButtonPressedHandler(2)) { playerX -= 5; } else if(gamepadButtonPressedHandler(3)) { playerX += 5; } if(gamepadButtonPressedHandler(11)) { alert('BOOM!'); } // ... }
こうすることで、プレイヤーの機体を DPad
ボタン(十字キー)で操作したり、A
ボタンで爆弾を爆発させることができるようになります。このほか、スティック操作を検知することもできますし、独自の小さいライブラリを作ってゲームパッドからの入力を処理することもできます。詳しくは、Desktop gamepad controls をご覧ください。
特殊な操作方法
さらにもっと進んで、リビングにある大型 TV のリモコンを使ったり、ラップトップの前で手を振ったり、食材とPCを線で繋いでその食材を押したりすることで、ゲームをプレイすることも可能です。
例えば、Panasonic 製の TV リモコンはキーボードイベントを再利用しているので、対応が非常に簡単です。リモコンの方向矢印はキーボードのカーソルキーと全く同じコード(37
、38
、39
、40
)を使用しているので、すぐに対応できます。リモコン特有のボタンについては、こちらに一覧がありますので、詳細情報とともにご覧ください。
また、リモコンのボタンを押す代わりに、Leap Motion 機器の機能を使用して自分の手の位置や他のパラメーターを検知することで、何にも触れることなくプレイヤーの機体を操作することもできます。あらかじめ定義されている loop 内で、手に関する情報を取得できるので、
Leap.loop({ hand: function(hand) { horizontalDegree = Math.round(hand.roll() * toDegrees); verticalDegree = Math.round(hand.pitch() * toDegrees); grabStrength = hand.grabStrength; } });
それを使用してプレイヤーの位置を更新します。
function draw() { // ... if(horizontalDegree > degreeThreshold) { playerX -= 5; } else if(horizontalDegree < -degreeThreshold) { playerX += 5; } if(verticalDegree > degreeThreshold) { playerY += 5; } else if(verticalDegree < -degreeThreshold) { playerY -= 5; } if(grabStrength == 1) { alert('BOOM!'); } // ... }
その他にも、Unconventional controlsでは、 ドップラー効果や Proximity API、MaKey MaKeyなど興味深い操作方法の実装方法を紹介しています。
まとめ
最近は、HTML5 ゲームをプレイするのに使える機器がますます増えています。ご自身の腕時計も使えるかもしれませんし、はたまた音声認識によるウェブゲームも良いかもしれません。まさに、可能性は無限です。覚えておいて頂きたいのは、多くの操作方法に対応しているほど、ゲームにとっては良いことだということです。なぜなら、幅広い種類の機器を用いて、どんなプラットフォームでもプレイ可能になるからです。ぜひ、ブラウザがもたらす可能性を存分に活用しましょう。