Roll It は、スマートフォンやパソコンのブラウザのみを使用して、定番のボードゲームを再現した Chrome 試験運用版です。スマートフォンのブラウザでは、手首をひねってボールを狙って転がすことができ、パソコンのブラウザでは、WebGL と Canvas を使用して Roll It のレーンにリアルタイムのグラフィックをレンダリングします。2 つのデバイスは WebSocket を介して通信します。アプリはありません。ダウンロードは不要です。トークンはありません。最新のブラウザさえあれば、
Google Creative Lab の指示のもと、Legwork がユーザー エクスペリエンス、インターフェース、ゲーム環境を開発し、開発パートナーの Mode Set と連携して Roll It を構築しました。プロジェクト期間中は、いくつかの独自の課題がありました。この記事では、Roll It を完成させるために使用した手法、発見したコツ、学んだ教訓をご紹介します。
3D ワークフロー
最初は、ソフトウェアからウェブ対応のファイル形式に 3D モデルを変換する最善の方法を見つけるのに苦労しました。Cinema 4D でアセットを作成した後、モデルを簡素化して低ポリゴン メッシュに変換しました。各メッシュには、オブジェクトの部分を区別して着色とテクスチャリングを行うための特定のポリゴン選択タグが付けられています。その後、Collada 1.5(.dae)ファイルとしてエクスポートし、オープンソースの 3D プログラムである Blender にインポートして、three.js と互換性のあるファイルを作成できました。モデルが正しくインポートされたことを確認したら、メッシュを JSON ファイルとしてエクスポートし、コードを使用して照明を適用しました。具体的な手順は次のとおりです。
コードの記述
Roll It はオープンソース ライブラリで開発され、最新のブラウザでネイティブに動作します。WebGL や WebSocket などの技術により、ウェブはコンソール品質のゲームやマルチメディア エクスペリエンスに近づきつつあります。HTML 開発に利用できる最新のツールが登場したことで、デベロッパーがこうしたエクスペリエンスを簡単に構築できるようになりました。
開発環境
Roll It の元のコードの大半は CoffeeScript で記述されています。これは、整形式でlint が適用された JavaScript にトランスコンパイルされる、簡潔でわかりやすい言語です。CoffeeScript は、優れた継承モデルとクリーンなスコープ処理により、OOP 開発に適しています。CSS は SASS フレームワークで記述されています。このフレームワークには、プロジェクトのスタイルシートを拡張して管理するための優れたツールが多数用意されています。これらのシステムをビルドプロセスに追加するには、設定に少し時間がかかりますが、特に Roll It のような大規模なプロジェクトでは、その効果は確実に得られます。開発中にアセットを自動コンパイルするように Ruby on Rails サーバーを設定したため、これらのコンパイル手順はすべて透過的になりました。
スムーズで快適なコーディング環境を構築するだけでなく、リクエストを最小限に抑えてサイトの読み込みを高速化するため、アセットを手動で最適化しました。すべての画像を ImageOptim と ImageAlpha の 2 つの圧縮プログラムで実行しました。各プログラムは、それぞれ可逆圧縮と非可逆圧縮という独自の方法で画像を最適化します。適切な設定を組み合わせることで、画像のファイルサイズを大幅に削減できます。これにより、外部画像の読み込み時に帯域幅を節約できるだけでなく、画像を最適化すると、HTML、CSS、JavaScript にインラインで埋め込むための、はるかに小さい base64 エンコード文字列に変換されます。Base64 エンコードに関連して、この手法を使用して Open Sans WOFF フォント ファイルと SVG フォント ファイルを CSS に直接埋め込み、リクエストの合計数をさらに削減しました。
物理学が有効な 3D シーン
THREE.js は、ウェブ向けの汎用 3D JavaScript ライブラリです。低レベルの 3D 数学とハードウェアベースの WebGL 最適化をラップアップしているため、カスタム シェーダーを記述したり、手動で行列変換を実行したりすることなく、照明がよく当たった美しいインタラクティブな 3D シーンを簡単に作成できます。Physijs は、JavaScript に変換された一般的な C++ 物理ライブラリの THREE.js 固有のラッパーです。このライブラリを利用して、ボールが転がり、ジャンプし、目的地に向かってバウンドする様子を 3D でシミュレートしました。
最初から、ボールを転がす物理的な体験をリアルに感じさせるだけでなく、ゲーム内のオブジェクトをリアルに感じさせることを目標としていました。これには、Physijs シーンの全体的な重力、プレーヤーが投げたボールが転がる際の速度、レーンのジャンプの傾斜、ボールとレーンの素材の摩擦と反発(弾力性)のプロパティを何度も調整する必要がありました。重力と速度の増加により、よりリアルなゲーム体験が実現しました。
スムージング
最新のブラウザとビデオカードの組み合わせのほとんどは、WebGL 環境でネイティブのハードウェア ベースのアンチエイリアシングを利用できますが、一部の組み合わせではうまく機能しません。アンチエイリアシングがネイティブで機能しない場合、THREE.js シーンのエッジがくっきりとコントラストが強すぎると、(少なくとも目が肥えた人には)ギザギザして見栄えが悪くなります。
幸い、修正方法があります。コード スニペットを使用して、プラットフォームがアンチエイリアスをネイティブにサポートするかどうかを検出できます。問題がなければ、そのまま使用できます。問題がある場合は、THREE.js に付属している一連のポスト プロセッシング シェーダーが役に立ちます。具体的には、FXAA アンチエイリアス フィルタです。このシェーダーを使用してレンダリングされたシーンをフレームごとに再描画することで、通常、線やエッジがよりスムーズになります。以下のデモをご覧ください。
// Check for native platform antialias support via the THREE renderer
// from: http://codeflow.org/entries/2013/feb/22/how-to-write-portable-webgl/#antialiasing
var nativeAntialiasSupport = (renderer.context.getParameter(renderer.context.SAMPLES) == 0) ? false : true;
加速度計ベースのゲーム コントロール
Roll It の魅力の多くは、プレイヤーがスマートフォンで行うボールを転がすジェスチャーにあります。モバイル デバイスでは、ブラウザ内で加速度計にアクセスできるようになってしばらく経ちますが、業界としてウェブでのモーション ベースのジェスチャー認識を検討し始めたのは最近のことです。スマートフォンの加速度計から得られるデータには限界がありますが、少し工夫すれば、素晴らしい新しいエクスペリエンスを実現できます。
ロールの検出 メインの「ロール」ジェスチャーは、ウィンドウの deviceorientation
イベントから取得した最新の 10 件の加速度計の更新をトラッキングすることから始まります。現在の傾斜値から前の傾斜値を減算することで、イベント間の角度のデルタを保存します。その後、過去 10 回の角度の差分を常に合計することで、スマートフォンが空間を移動する際の継続的な回転を検出できます。スマートフォンがスイープ角度の変化のしきい値を超えると、ロールがトリガーされます。次に、そのスイープで最も大きな単一の傾斜変化を見つけることで、ボールの速度を推定できます。Roll It では、この速度は、各加速度計の更新に付加されるタイムスタンプを使用して正規化されます。これにより、さまざまなデバイスで加速度計の更新がブラウザにストリーミングされる速度のばらつきを抑えることができます。
WebSocket 通信
プレーヤーがスマートフォンでボールを転がすと、ボールを打ち上げるようスマートフォンからノートパソコンにメッセージが送信されます。この「ロール」メッセージは、2 台のマシン間の WebSocket 接続を介して JSON データ オブジェクトを介して送信されます。JSON データは小さく、主にメッセージ タイプ、投擲速度、照準方向で構成されています。
{
"type": "device:ball-thrown",
"speed": 0.5,
"aim": 0.1
}
ノートパソコンとスマートフォン間の通信はすべて、このような小さな JSON メッセージを通じて行われます。ゲームがパソコンで状態を更新するたび、またはユーザーがスマートフォンでボタンを傾けたりタップしたりするたびに、マシン間で WebSocket メッセージが送信されます。この通信をシンプルで管理しやすくするため、WebSocket メッセージはどちらかのブラウザから単一の終了ポイントを使用してブロードキャストされます。一方、受信側のブラウザにはエントリ ポイントが 1 つあり、1 つの WebSocket オブジェクトが両端のすべての受信メッセージと送信メッセージを処理します。WebSocket メッセージが受信されると、jQuery の trigger()
メソッドを使用して、JavaScript アプリ内で JSON データが再ブロードキャストされます。この時点で、受信データは他のカスタム DOM イベントと同様に動作し、アプリ内の他のオブジェクトによって取得および処理できます。
var websocket = new WebSocket(serverIPAddress);
// rebroadcast incoming WebSocket messages with a global event via jQuery
websocket.onmessage = function(e) {
if (e.data) {
var obj = JSON.parse(e.data);
$(document).trigger(data.type, obj);
}
};
// broadcast outgoing WebSocket messages by passing in a native .js object
var broadcast = function(obj) {
websocket.send(JSON.stringify(obj));
};
Roll It の WebSocket サーバーは、2 台のデバイスがゲームコードと同期されると、その場で作成されます。Roll It のバックエンドは、Google Compute Engine と App Engine プラットフォームで Go を使用して構築されています。
メニュー画面の傾斜
ゲームプレイ中に使用されるイベントドリブンの WebSocket メッセージに加えて、Roll It のメニューは、スマートフォンを傾けてボタンをタップして選択を確定することで操作します。そのためには、スマートフォンからノートパソコンに送信される傾斜データのストリームの安定性が求められます。帯域幅を削減し、不要な更新を送信しないようにするため、これらのメッセージは、デバイスの傾斜が数度以上変化した場合にのみ送信されます。スマートフォンがテーブルの上に置かれている場合、傾斜データのストリームを送信しても意味がありません。送信レートもスロットリングされます。デバイスが積極的に傾けられていても、Roll It では 1 秒あたり 15 件以下の WebSocket メッセージしか送信されません。
傾斜値がコンピュータで検出されると、requestAnimationFrame
を使用して時間の経過とともに補間され、スムーズな操作感を維持します。最終的な結果は、回転するメニューと、ユーザーの選択を示すために転がるボールです。スマートフォンが傾斜データを送信すると、requestAnimationFrame
ループ内で CSS 変換が再計算され、これらの DOM 要素がリアルタイムで更新されます。メニューのコンテナは単に回転しますが、ボールは床を転がっているように見えます。この効果を実現するために、基本的な三角関数を実装して、ボールの x 座標をその回転に関連付けます。簡単な式は、回転数 = x ÷(直径 * π)です。
まとめ
Roll It は時代の流れです。開発を支えるオープンソース プロジェクト、デスクやポケットに収まるデバイスの処理能力、プラットフォームとしてのウェブの状態を考えると、オープンウェブに接続するのは本当にエキサイティングで変革的な時代です。ほんの数年前まで、こうした技術の多くは独自のシステムにのみ存在し、自由に使用、配布することはできませんでした。今日、パズルの新しいピースを毎日作成して共有することで、複雑なエクスペリエンスを少ない労力で実現できます。ぜひご自身で素晴らしい作品を作成して、世界と共有しましょう。