HTML5 の Canvas を使用して作成したゲームでユーザー入力を処理する

ゲーム開発に適したキーボード・イベント、マウス・イベント、タッチ・イベントをキャプチャーする

HTML5 のゲームの世界に足を踏み入れる時点では、キーボード、マウス、およびタッチ・ベースの入力を管理する難しさを見くびりがちです。この記事では、HTML5 の Canvas を使用して作成したゲームでユーザー操作の処理をするための基本技術を探ります。キーボード・イベントとマウス・イベントを処理する方法、Web ブラウザーのイベントに対するデフォルトの動作を変更する方法、イベントをゲーム・オブジェクトの論理表現にブロードキャストする方法を説明するのに加え、iPhone や iPad などのモバイル機器で、機器の種類を問わずにユーザー入力を処理する方法も説明します。

Kevin Moot, Software Developer, The Nerdery

Photo of Kevin MootKevin Moot は、子供の頃に自分の (ずらりと 6 色を揃え、驚異的な 280x192 の解像度を持つ) Apple IIe でゲームを作成して以来、コンピューター・グラフィックスに興味を持っています。HTML5 の Canvas 技術を使用して最先端の Web サイトの数々を作成した彼は、HTML/CSS、JavaScript、.NET などを専門分野にしています。彼は現在、The Nerdery でインタラクティブ・ソフトウェアの開発者として働いています。



2012年 8月 30日

はじめに

Flash や Silverlight を扱った経験がある開発者の多くが、HTML5 の Canvas を使用して作成されたアプリケーションで意外に感じるのは、ユーザー入力の処理という点に関して特に調整されてはいないことです。JavaScript 対応の Web ブラウザーが登場してからというもの、HTML でユーザー入力を処理するには、基本的にブラウザーに組み込まれたイベント処理システムが使用されます。ユーザー入力の検出および処理という点で、HTML5 に固有の部分は何もありません。例えば、ユーザーがクリックした座標 (x, y) を示すための下位レベルのフィードバックは、ブラウザーが提供できるため、それを利用すればよいのです。

頻繁に使用される略語

  • CSS: Cascading Style Sheets
  • DOM: Document Object Model
  • HTML: HyperText Markup Language

ユーザー操作の処理は、ゲームのアーキテクチャーにおける下位レベルの処理とまったく変わりありません。ユーザーが Canvas にレンダリングされた特定のオブジェクトを操作したことを通知するための抽象化は組み込まれていません。したがって、これらのイベントをどのように処理するかについては、かなり細かいレベルで制御することができます。さまざまなブラウザーの特異な癖を切り離しておくことができる限り、イベント処理を特定の実装に結び付けるのではなく、特定のアプリケーションに合わせてイベント処理を調整し、最大限の効率性を引き出すことができます。

この記事を読んで、HTML5 の Canvas を使用したゲームでユーザー操作の処理をするための基本技術を学んでください。記事では、キーボード、マウス、およびタッチ・ベースのイベントを処理する方法を具体的な例で説明します。さらに、イベントをゲーム・オブジェクトにブロードキャストする際のストラテジー、およびモバイルとの互換性についても取り上げます。

記事で使用するサンプル・プログラムのソース・コードは、「ダウンロード」セクションからダウンロードすることができます。


イベントのタイプ

ユーザー操作は、一貫してブラウザーのイベント・リスナーによって処理されます。これは従来からのモデルであり、HTML5 の出現によって新しくなったところは何もありません。Netscape Navigator の初期の頃から使われてきたイベント・モデルがそのまま使用されます。

基本的に、インタラクティブなアプリケーションやゲームとは、ユーザー入力に対応するブラウザー・イベント・モデルとグラフィカルな出力に対応する Canvas の組み合わせだと考えてください。自分で作らない限り、この 2 つの間に論理的なつながりはありません。

この組み合わせでは、イベント・リスナーを <canvas> 要素自体にアタッチできるという事実を利用することになります。<canvas> 要素は単なるブロック・レベルの要素に過ぎないため、ブラウザーで行う処理の観点からは、この要素にイベント・リスナーをアタッチするのは、<div> やその他のブロック・レベルの要素にイベント・リスナーをアタッチすることと何ら変わりはありません。


キーボード・イベント

リッスンして処理する対象となるイベントの中で最も単純なタイプは、キーボード・イベントです。キーボード・イベントは、Canvas にも、ユーザーが操作するカーソルの位置にも依存しません。キーボード・イベントで必要となるのは、文書レベルで keydown、keyup、および keypress のイベントをリッスンすることだけです。

キーボード・イベントをリッスンする

イベント・リスナー・モデルはブラウザー実装によって異なる場合があるため、ライブラリーを使用してイベントの処理を正規化することが、最も早くイベント処理を開始する方法となります。以下の例では、イベントをバインドするために jQuery を使用します。一般に、手始めとしては jQuery を使用するのが最も簡単ですが、レガシー・ブラウザーに対応するための取り組みにより、jQuery はかなり複雑な作りとなっていることから、パフォーマンスに影響が出る可能性があります。よく使われているライブラリーには、特定のブラウザーに依存しない高速なキーボード・イベント処理を目的に作成された Kibo もあります (「参考文献」を参照)。

リスト 1 に、キー・イベントをリッスンし、押下されたキーに応じて適切なアクションを実行する例を記載します。

リスト 1. キーボード・イベントの処理
$(document.body).on('keydown', function(e) {
    switch (e.which) {
        // key code for left arrow
        case 37:
            console.log('left arrow key pressed!');
            break;
        
        // key code for right arrow
        case 39:
            console.log('right arrow key pressed!');
            break;
    }
});

アプリケーションが Web ブラウザーの環境で実行されるとしたら、実用にかなったキーの組み合わせを用いることを念頭に置くことが重要です。一般によく使われる特定のキーの組み合わせ (例えば、Ctrl+r など) に対する動作を定義し、その組み合わせに対するブラウザーのデフォルトの動作を変更することは技術的には可能ですが、それを行うと、かなりのひんしゅくを買うことになります。


マウス・イベント

マウス・イベントは、キーボード・イベントよりも複雑です。マウス・イベントの場合、ブラウザー・ウィンドウ内での Canvas の位置と、ユーザーのマウス・カーソルが置かれている位置を認識しなければなりません。

マウス・イベントをリッスンする

ブラウザー・ウィンドウ全体を基準としたマウスの相対位置は、e.pageX および e.pageY プロパティーを使用して簡単に取得することができます。この場合、座標の原点 (0, 0) はブラウザー・ウィンドウの左上隅に位置します。

通常、ユーザーのマウス・カーソルが Canvas 領域内に位置していない場合は、ユーザー入力についてあまり気にすることはありません。したがって、Canvas の左上隅に座標原点 (0, 0) を配置するのが望ましく、理想的なのはブラウザー・ウィンドウ全体に対するグローバル座標系ではなく、Canvas 領域に対するローカル座標系で処理をすることです。

マウス・イベントのストラテジー

グローバル・ウィンドウの座標をローカルの Canvas 座標に変換するには、以下の手順に従います。

  1. ページ上の Canvas DOM 要素の (x, y) 位置を計算します。
  2. 文書全体に対するマウスのグローバル位置を判別します。
  3. グローバル座標を、Canvas の左上隅を原点 (0, 0) とする相対座標に有効に変換するためには、ステップ 2 で計算したマウスのグローバル位置と、ステップ 1 で計算した Canvas の位置の差を求めます。

図 1 に、グローバル座標系に関して取得しなければならない情報の例を示します。

図 1. グローバル座標でのマウスの位置
文書のグローバル (x, y) 座標が (300, 200)、マウスのグローバル (x, y) 座標が (350, 260) となっているウィンドウを示す画面

図 2 に、マウスの位置をローカル座標に変換した結果を示します。

図 2. ローカル座標に変換されたマウスの位置座標
マウスのローカル (x, y) 座標を (50, 60) として示す画面

リスト 2 に、マウスのローカル座標を判定する方法を示します。ここでは、マークアップに <canvas> 要素が <canvas id="my_canvas"></canvas> として定義されていることを前提とします。

リスト 2. マウス・イベントの処理
var canvas = $('#my_canvas');

// calculate position of the canvas DOM element on the page

var canvasPosition = {
    x: canvas.offset().left,
    y: canvas.offset().top
};

canvas.on('click', function(e) {

    // use pageX and pageY to get the mouse position
    // relative to the browser window

    var mouse = {
        x: e.pageX - canvasPosition.x,
        y: e.pageY - canvasPosition.y
    }

    // now you have local coordinates,
    // which consider a (0,0) origin at the
    // top-left of canvas element
});

回避したいブラウザーの動作

コンピューター・ゲームでは一般に、ブラウザーのデフォルトの動作とゲームのアクションが干渉することは望ましくありません。例えば、マウスをドラッグするとテキストが選択されたり、マウスを右クリックするとコンテキスト・メニューが開いたり、マウス・ホイールをスクロールするとページが上下に移動したりするなどの動作は避けなければなりません。

図 3 に一例として、ユーザーがブラウザーで画像をクリックしてドラッグした場合の動作を示します。ブラウザーのこのデフォルトの動作は、ドラッグ・アンド・ドロップ式のアプリケーションではまったく妥当な動作ですが、ゲームでは望まれない動作です。

図 3. 画像をドラッグしたときのブラウザーのデフォルトの動作
グラフィック画像をドラッグ操作で移動している様子

すべてのイベント・ハンドラー内に preventDefault() の行を追加し、この関数から false が返されるようにしてください。リスト 3 のコードでは、ブラウザーのデフォルトのアクションが行われないようにすると同時にイベントのバブリングが起こらないようにしています。

リスト 3. デフォルトの動作の防止
canvas.on('click', function(e) {
    e.preventDefault();
    
    var mouse = {
        x: e.pageX - canvasPosition.x,
        y: e.pageY - canvasPosition.y
    }
    
    //do something with mouse position here
    
    return false;
});

リスト 3 のコードを使用した場合でも、ユーザーが DOM 要素上でドラッグ操作を開始すると、I ビーム・カーソルが表示されたり、テキストが選択されたりするなど、望ましくない副次作用が生じる可能性はあります。従来、画像をドラッグしたときのこの問題はより一般的なものですが、それでもドラッグによって要素が選択されるという動作を防ぐためには、Canvas にもリスト 3 のコードを適用するのが得策です。リスト 4 に記載する CSS ルールでは、多少の CSS を加えることによって、選択という副次作用を防ぐようになっています。

リスト 4. 選択動作を防止するために推奨されるスタイル
image, canvas {
    user-select: none;
    -ms-user-select: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -webkit-touch-callout: none;
    -webkit-user-drag: none;
}

デスクトップの動作を変更する

一般に、ドラッグ・イベントと選択イベントの処理を変更することで、ブラウザーでのドラッグによるデフォルトの動作と選択によるデフォルトの動作が行われないようにするのが賢明です。

リスト 5 のコードでは、意図的に、イベントをアタッチする手段として jQuery を使用していません。jQuery は、ondragstart イベントと onselectstart イベントを適切に処理しないためです (jQuery を使ってイベントをアタッチすると、イベント・ハンドラーが起動されることは決してないでしょう)。

リスト 5. ドラッグ・イベントと選択イベントのキャンセル
var canvasElement = document.getElementById('my_canvas');

// do nothing in the event handler except canceling the event
canvasElement.ondragstart = function(e) {
    if (e && e.preventDefault) { e.preventDefault(); }
    if (e && e.stopPropagation) { e.stopPropagation(); }
    return false;
}

// do nothing in the event handler except canceling the event
canvasElement.onselectstart = function(e) {
    if (e && e.preventDefault) { e.preventDefault(); }
    if (e && e.stopPropagation) { e.stopPropagation(); }
    return false;
}

モバイルの動作を変更する

モバイル機器では、ユーザーがブラウザー・ウィンドウをズームしたり、パンしたりできないようにすることが不可欠となることは珍しくありません (多くの場合、モバイル・ブラウザーでは、タッチ・ジェスチャーに対するデフォルトの動作はズームとパンです)。

ズームの動作は、viewport メタ・タグに user-scalable=no を追加することで防止することができます。以下はその一例です。

<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1" />

文書またはウィンドウに対してジェスチャーを使ったすべての操作を無効にするには、リスト 6 に記載するイベント・リスナーを document.body ファイルにアタッチします。これにより、ユーザーが Canvas やゲーム領域の外側をタップしたとしても、基本的にブラウザーのデフォルトの動作は実行されなくなります。

リスト 6. モバイル機器のウィンドウに対してジェスチャーを使ったすべての操作を無効にする
document.body.ontouchstart = function(e) {
    if (e && e.preventDefault) { e.preventDefault(); }
    if (e && e.stopPropagation) { e.stopPropagation(); }
    return false;
}

document.body.ontouchmove = function(e) {
    if (e && e.preventDefault) { e.preventDefault(); }
    if (e && e.stopPropagation) { e.stopPropagation(); }
    return false;
}

ゲーム・オブジェクトへのブロードキャスト

Canvas にアタッチするイベント・リスナーは、キャプチャー対象とするイベント・タイプごとに 1 つだけにする必要があります。例えば、クリック・イベントとマウスムーブ・イベントをキャプチャーする必要がある場合、1 つのクリック・イベント・リスナーと 1 つのマウスムーブ・イベント・リスナーを Canvas にアタッチします。これらのイベント・リスナーをアタッチする必要があるのは一度だけなので、通常はアプリケーションの初期化中に、該当するイベントをアタッチします。

イベント・リスナーでキャプチャーした有用な情報を Canvas 上にレンダリングされたオブジェクトに伝播する必要がある場合には、そのシステム用の独自のロジックを作成しなければなりません。この例では、そのようなシステムが、クリック・イベントやマウスムーブ・イベントを、そのいずれかのイベントの処理に関連するすべてのゲーム・オブジェクトにブロードキャストする役割を担うことになります。

それぞれのゲーム・オブジェクトがこれらのイベントのうちのいずれかを認識した場合、ゲーム・オブジェクトにとってまず必要となるのは、クリック・イベントやマウスムーブ・イベントがそのゲーム・オブジェクトに関連するかどうかを明らかにすることです。関連する場合、ゲーム・オブジェクトに次に必要となるのは、マウスの位置座標がそのオブジェクトの境界内に位置しているかどうかを判定することです。

ブロードキャストのストラテジー

ストラテジーは、ゲームのタイプによって異なります。例えば、2D タイルセットには、3D の世界とは異なるストラテジーがあります。

以下のステップでは、単純な 2D アプリケーションの場合に有効に機能する単純な実装を概説します。

  1. Canvas 領域内でユーザーがマウスをクリックした位置の座標を検出します。
  2. すべてのゲーム・オブジェクトに、特定の座標でクリック・イベントが発生したことを通知します。
  3. それぞれのゲーム・オブジェクトについて、マウス座標とゲーム・オブジェクトの境界ボックスの間でヒット・テストを実行し、マウスの位置座標がそのオブジェクトに当たるかどうかを判定します。

単純なブロードキャストの例

リスト 7 は、ブロードキャストに対処するクリック・イベント・ハンドラーの一例です。この例では、世界中のすべてのゲーム・オブジェクトを追跡するための何らかの構造がすでにセットアップされていることを前提とします。すべてのゲーム・オブジェクトの位置とサイズは、gameObjectArray という名前の変数に格納されます。

リスト 7. ゲーム・オブジェクトにブロードキャストするクリック・イベント・ハンドラー
// initialize an array of game objects
// at various positions on the screen using
// new gameObject(x, y, width, height)

var gameObjectArray = [
	new gameObject(0, 0, 200, 200),
	new gameObject(50, 50, 200, 200),
	new gameObject(500, 50, 100, 100)
];

canvas.on('click', function(e) {
    var mouse = {
        x: e.pageX - canvasPosition.x,
        y: e.pageY - canvasPosition.y
    }
    
    // iterate through all game objects 
    // and call the onclick handler of each

    for (var i=0; i < gameObjectArray.length; i++) {
        gameObjectArray[i].handleClick(mouse);
    }
});

次のステップでは、マウスの位置座標がゲーム・オブジェクトの境界ボックスの領域内に入っているかどうかを判定するために、各ゲーム・オブジェクトがヒット・テストを確実に実行できるようにします。図 4 に、ヒット・テストを実行して不合格になる例を示します。

図 4. 境界外をクリックした場合のヒット・テストは不合格になります
境界の左上隅の (x, y) 座標が (120, 150) の場合に、(x, y) 座標が (50, 60) の場所をクリックした様子を示す画面

図 5 に、ヒット・テストを実行して合格になる例を示します。

図 5. 境界内をクリックした場合のヒット・テストは合格になります
境界の左上隅の (x, y) 座標が (120, 150) の場合に、(x, y) 座標が境界内にある (160, 170) の場所をクリックした様子を示す画面

ゲーム・オブジェクトのクラスは、リスト 8 のように定義することができます。ヒット・テストは、onclick() 関数の中で実行されます。このテストでは、オブジェクトの長方形の境界ボックスと、パラメーターとして渡されたマウスの位置座標とが衝突するかどうかを判定します。

リスト 8. ゲーム・オブジェクト・クラスとヒット・テスト
function gameObject(x, y, width, height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    
    // mouse parameter holds the mouse coordinates
    this.handleClick = function(mouse) {
        
        // perform hit test between bounding box 
        // and mouse coordinates

        if (this.x < mouse.x &&
            this.x + this.width > mouse.x &&
            this.y < mouse.y &&
            this.y + this.height > mouse.y) {

            // hit test succeeded, handle the click event!
            return true;
        }
        
        // hit test did not succeed
        return false;
    }
}

ブロードキャストを効率化する

多くの場合は、上記のような実装よりも効率的な実装をすることができます。例えば、ゲームで数千ものゲーム・オブジェクトを使用しているとしたら、イベントが発生するたびに、そのシーンにおけるすべてのゲーム・オブジェクトをテストすることは間違いなく避けたいはずです。

以下の例では、jQuery カスタム・イベントを使用して合成イベントが発生するようにします。合成イベントは、その特定のイベントをリッスンするゲーム・オブジェクトでしか処理されません。この例のステップは以下のとおりです。

  1. マウス・クリック・イベントを前と同じように処理し、必要に応じて変換を行います (マウスの位置座標をローカル座標に変換するなど)。
  2. 変換されたマウス座標をパラメーターとして格納した合成イベントを発生させます。
  3. クリック・イベントの処理に関連するゲーム・オブジェクトは、この合成イベントをリッスンするリスナーをセットアップします。

マウス・クリック・イベント・ハンドラーは、単にカスタム・イベントだけをトリガーするように変更されます。カスタム・イベントには任意の名前を指定することができます。リスト 9 では、handleClick という名前になっています。

リスト 9. カスタム・イベントをトリガーする
canvas.on('click', function(e) {
    var mouse= {
        x: e.pageX - canvasPosition.x,
        y: e.pageY - canvasPosition.y
    }
         
    //fire off synthetic event containing mouse coordinate info
    $(canvas).trigger('handleClick', [mouse]);
});

リスト 10 に示されているように、ゲーム・オブジェクト・クラスも変更されています。変更後のクラスは、onclick 関数を定義する代わりに、単純に handleClick イベントをリッスンするだけに過ぎません。handleClick イベントがトリガーされると常に、このイベントをリッスンしているすべてのゲーム・オブジェクトが、それに対応するイベント・ハンドラーを起動します。

リスト 10. カスタム・イベントの処理
function gameObject(x, y, width, height) {
    var self = this;
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    
    $(canvas).on('handleClick', function(e, mouse) {

        // perform hit test between bounding box 
        // and mouse coordinates

        if (self.x < mouse.x &&
            self.x + self.width > mouse.x &&
            self.y < mouse.y &&
            self.y + self.height > mouse.y) {

            // hit test succeeded, handle the click event!

        }
    });
}

高度なヒット・テスト

複数のゲーム・オブジェクトが互いに重なり合っていると、どのようなことになるかを検討することは重要なことです。複数のオブジェクトが重なっている場所をユーザーがクリックした場合に、その動作をどのように処理するかを決定する必要があります。一般には、一番上にあるオブジェクトのイベント・ハンドラーが起動されて、そのオブジェクトの下にあるその他のオブジェクトは無視されるという動作を期待することでしょう。

このような重なり合ったゲーム・オブジェクトを扱うには、オブジェクトそれぞれの順序、つまり深さを知る必要があります。Canvas は論理的な層の表現を公開しないため、この場合もやはり開発者が主導して、この状況に対処するために必要なロジックを作成しなければなりません。

深さの概念を導入するには、すべてのゲーム・オブジェクトにその深さを表現する z インデックスを割り当てる必要があります。リスト 11 に一例を記載します。

リスト 11. ゲーム・オブジェクトに z インデックスを追加する
function gameObject(x, y, zIndex, width, height) {
    var self = this;
    this.x = x;
    this.y = y;
    this.zIndex = zIndex;
    this.width = width;
    this.height = height;

    //...
}

深さのテストを容易にするためには、ソートを行う必要があります。リスト 12 は、最も大きな値の z インデックスを持つゲーム・オブジェクトがリストの先頭にくるようにゲーム・オブジェクトをソートして格納するための構造の例です。

リスト 12. ゲーム・オブジェクト配列のソート
// sort in order such that highest z-index occurs first
var sortedGameObjectArray = gameObjectArray.sort(function(gameObject1, gameObject2) {
    if (gameObject1.zIndex < gameObject2.zIndex) return true;
    else return false;        
})

最後に click 関数の中で、ソート済みの配列に含まれるすべてのゲーム・オブジェクトが順に処理されて、テストが実行されるようにします。

ヒット・テストの結果が合格となるゲーム・オブジェクトが見つかると同時に一連のヒット・テストを終了し、クリックの伝播を停止します。リスト 13 とは異なり、テストを終了しない場合には、さらに深いところにあるゲーム・オブジェクトが click イベントを処理するという望ましくない動作が続けられることになります。

リスト 13. ヒット・テストに合格した際には一連のヒット・テストを終了する
canvas.on('click', function(e) {
    var mouse = {
        x: e.pageX - canvasPosition.x,
        y: e.pageY - canvasPosition.y
    }
               
    for (var i=0; i < sortedGameObjectArray.length; i++) {
        var hitTest = sortedGameObjectArray[i].onclick(mouse);
        
        // stop as soon as one hit test succeeds
        if (hitTest) {
            break; // break out of the hit test
        }
    }
});

不規則な形のゲーム・オブジェクトの境界

通常、長方形の境界ボックスに対するヒット・テストは最も単純かつ効率的に実行できますが、多くの場合は長方形の境界ボックスをテストするだけでは事足りません。長方形よりも複雑な形状のゲーム・オブジェクトでは、三角形や多角形の境界領域に対してテストするほうが妥当です。その場合には、ゲーム・オブジェクトのイベント・ハンドラーのテスト・ロジックをより高度なヒット検出のテストに取り替えなければなりません。通常は、ゲームでの衝突に関する物理の領域で適切なロジックを参照することになります。

Canvas API には、多角形の衝突テストを実行できる IsPointInPath() という興味深い関数が用意されています。基本的に IsPointInPath(x, y) では、特定の (x, y) 地点が任意のパス (基本的に多角の境界) 内に入っているかどうかをテストすることができます。この関数は、提供された (x, y) 座標が Canvas コンテキストで定義された現行のパス内に入っていれば、true を返します。

isPointInPath() を使用する

図 6 に、長方形ではないパスに対してマウスの位置座標を判定しなければならない場合を示します。この場合のパスは単純な三角形です。

図 6. 三角形のパス境界内でのクリック
黒い三角形の内側にある (x, y) 座標 (20, 50) をマウス・ポインターがクリックしている様子

このパスが塗りつぶされているのは、単にパスを見やすくするためです。パスを画面上で実際にレンダリングしなくても、IsPointInPath() は有益な結果を返すことができます。したがって、パスを実際に描画するには、fill() stroke() を呼び出さなくてもパスを定義するだけで十分です。リスト 14 に詳細を示します。

リスト 14. isPointInPath を使用したヒット検出
$(canvas).on('handleClick', function(e, mouse) {

    // first, define polygonal bounding area as a path
    context.save();
    context.beginPath();
    context.moveTo(0,0);
    context.lineTo(0,100);
    context.lineTo(100,100);
    context.closePath();
    
    // do not actually fill() or stroke() the path because
    // the path only exists for purposes of hit testing
    // context.fill();
    
    // perform hit test between irregular bounding area
    // and mouse coordinates
    if (context.isPointInPath(mouse.x, mouse.y)) {
        // hit test succeeded, handle the click event!
        
    }
    context.restore();
});

IsPointInPath() を使用するよりも、衝突アルゴリズムを自分で作成するほうが効率的なことはよくありますが、プロトタイプを作成したり、開発を迅速に行ったりするには、IsPointInPath() を使用するのが有効な手段となります。


モバイルへの対応

サンプル・ゲームをモバイル機器に対応させるには、マウス・イベントではなく、タッチ・イベントを処理しなければなりません。

モバイル・ブラウザーでは、指のタップ操作をクリック・イベントとして解釈することもできますが、一般にはクリック・イベントをリッスンすることのみに頼るのは手法として適切ではありません、それよりも賢明な手法は、最適な応答性を保証するために、特定のタッチ・イベントに対応するリスナーをアタッチすることです。

タッチ・イベントを検出する

最初に機器がタッチ・イベントをサポートするかどうかを検出し、その結果に応じてマウスの位置座標かタッチの位置座標のいずれかを返すヘルパー関数を作成することができます。これにより、ユーザーがデスクトップ・プラットフォームとモバイル・プラットフォームのどちらを使っているかとは無関係に、呼び出し側関数が入力座標を処理できるようになります。

リスト 15 に、マウス・イベントおよびタッチ・イベントをキャプチャーして応答を正規化する、特定の機器に依存しない関数の例を記載します。

リスト 15. マウス・イベントおよびタッチ・イベントを正規化する
function getPosition(e) {
    var position = {x: null, y: null};
    
    if (Modernizr.touch) { //global variable detecting touch support
        if (e.touches && e.touches.length > 0) {
            position.x = e.touches[0].pageX - canvasPosition.x;
            position.y = e.touches[0].pageY - canvasPosition.y;
        }
    }
    else {
        position.x = e.pageX - canvasPosition.x;
        position.y = e.pageY - canvasPosition.y;
    }
    
    return position;
}

タッチ・サポートを検出するために、この例では Modernizr ライブラリー (「参考文献」を参照) を使用しています。Modernizr ライブラリーを使用することにより、Modernizr.touch 変数の値を判定するだけで、タッチ・サポートを検出できるようになります。この判定では、機器がタッチ・イベントをサポートしている場合には true が返されます。

特定の機器に依存しないイベント・ハンドラー

イベント・リスナーを定義するために前に使ったコードは、アプリケーションの初期化中に、タッチ操作をサポートする機器とマウス入力それぞれの処理に分岐するコードに置き換えることができます。マウス・イベントをそれと同等のタッチ・イベントにマッピングするのは簡単です。例えば、mousedowntouchstart に、mouseuptouchend に置き換えればよいのです。

リスト 16 に、Modernizr を使用して同等のマウス・イベントとタッチ・イベントをマッピングする例を記載します。この例では、リスト 15 で定義した getPosition() 関数も使用しています。

リスト 16. 正規化したマウス・イベントとタッチ・イベントを使用する
var eventName = Modernizr.touch ? 'touchstart' : 'click';

canvas.on(eventName, function(e) {
    e.preventDefault();
    
    var position = getPosition(e);
    //do something with position here
    
    return false;
});

さらに高度なアクション (ピンチやスワイプなど) を処理する必要がない場合、デスクトップ・アプリケーションからマウス・イベントを直接移植するときには、この手法が一般に功を奏するはずです。ここではシングルタッチ・システムを前提としていますが、マルチタッチを検出しなければならない場合には、追加のコードが必要になります (それについては、この記事では説明しません)。


まとめ

この記事では、キーボード・イベントとマウス・イベントを処理する方法と、望ましくないブラウザーの動作を無効にする方法を学びました。さらに、イベントをゲーム・オブジェクトにブロードキャストする際のストラテジーを説明し、ヒット・テストに関するより高度な検討事項、そしてモバイルに対応させるための単純な手法についても検討しました。ユーザー入力には、この記事で取り上げたもの以外にも非常にさまざまなものがありますが、ここで取り上げた一般的なユーザー入力のシナリオは、HTML5 アプリケーションのユーザー入力を処理するための特定の機器に依存しない堅牢なライブラリーを作成する際の出発点となります。


ダウンロード

内容ファイル名サイズ
Article code listingsarticle.listings.zip5KB

参考文献

学ぶために

  • HTML5 の Canvas を使って素晴らしいグラフィックスを作成する」(developerWorks、2011年2月): Canvas を使用して Web ページを機能強化する方法を学んでください。Canvas は HTML5 の単純な要素ですが、強力な機能が満載されています。
  • HTML5 Canvas: このデモを見て、Canvas API の使用法と、極めて単純なアニメーションの色を設定する方法を学んでください。
  • HTML5 の基礎: 第 4 回 最後の仕上げ」(developerWorks、2011年7月): HTML5 の要素である Canvas について、その機能を具体的に説明する例から学んでください。
  • Canvas Pixel Manipulation: Safari Dev Center に用意されているこのデモは、Canvas を使って効果的なビジュアル・アセットを開発する好例です。
  • WHATWG: W3C と協力して HTML5 の微調整に取り組んでいるこの開発者コミュニティーについて調べてください。
  • Canvas tutorial: Mozilla の開発者たちが、独自の HTML ページに <canvas> 要素を実装する方法を説明しています。
  • HTML5 Canvas リファレンス: W3Schools.com に、Canvas の知識を深めるのに役立つエクササイズが用意されています。
  • jQuery Events API: ユーザーがブラウザーを操作するときの動作として、ある動作が適用されるように登録するためのメソッドや、登録した動作をさらに操作するためのメソッドについて学んでください。
  • developerWorks Web architecture ゾーン: さまざまな Web ベースのソリューションを話題にした記事を調べてください。広範な技術に関する記事とヒント、チュートリアル、標準、そして IBM Redbooks については、Web development の技術文書一覧を参照してください。
  • My developerWorks: developerWorks のエクスペリエンスを自分流にカスタマイズしてください。
  • developerWorks の Technical events and webcasts: これらのセッションで最新情報を入手してください。
  • developerWorks は Twitter を利用しています。今すぐ developerWorks のツイートをフォローしてください。
  • developerWorks オンデマンド・デモ: 初心者向けの製品のインストールおよびセットアップから熟練開発者向けの高度な機能に至るまで、さまざまに揃ったデモを見てください。

製品や技術を入手するために

  • jQuery: このよく使われている JavaScript ライブラリーをダウンロードしてください。HTML 文書のトラバース、イベント処理、アニメーション化、そして Ajax によるやりとりを単純化するこのライブラリーは、迅速な Web 開発に役立ちます。
  • Modernizr: HTML5 および CSS3 で駆動する次世代の Web サイトの作成に役立つ、オープンソースの JavaScript ライブラリーを入手してください。
  • Kibo: この評判の高い単純な JavaScript ライブラリーを使ってキーボード・イベントを処理してください。
  • IBM 製品の評価版: DB2、Lotus、Rational、Tivoli、および WebSphere のアプリケーション開発ツールとミドルウェア製品を体験するには、評価版をダウンロードするか、IBM SOA Sandbox のオンライン試用版を試してみてください。

議論するために

  • developerWorks コミュニティー: ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者によるブログ、フォーラム、グループ、ウィキを調べることができます。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=832304
ArticleTitle=HTML5 の Canvas を使用して作成したゲームでユーザー入力を処理する
publish-date=08302012