はじめに
通常、2D ゲームをプレイするとき、または HTML5 キャンバスをレンダリングするときは、複数のレイヤーを使用して複合シーンを構築するための最適化を実行する必要があります。 OpenGL や WebGL などの低レベルのレンダリングでは、フレームごとにシーンをクリーニングしてペイントすることによってレンダリングが実行されます。レンダリングを実装した後は、レンダリング量を減らすためにゲームを最適化する必要があり、コストは状況によって異なります。キャンバスは DOM 要素であるため、最適化の方法として複数のキャンバスを階層化できます。
一般的に使用される略語
この記事では、キャンバスをレイヤー化する理論的根拠を説明します。レイヤードキャンバスを実装するための DOM 設定を理解します。階層化を使用して最適化するには、さまざまな実践が必要です。この記事では、階層化アプローチを拡張するいくつかの最適化戦略の概念と手法についても説明します。
この記事で使用されている例のソース コードをダウンロードできます。
最適化戦略を選択します
最適な最適化戦略を選択するのは難しい場合があります。レイヤードシーンを選択するときは、シーンがどのように構成されているかを考慮する必要があります。大画面上で静止したオブジェクトをレンダリングするには、多くの場合、複数のコンポーネントを再利用する必要があり、それらは研究の優れた候補です。視差やアニメーション化されたエンティティなどの効果は、多くの場合、さまざまな画面スペースを大量に必要とします。最適な最適化戦略を検討するときは、これらの状況を認識しておくことをお勧めします。キャンバス レイヤの最適化にはいくつかの異なるテクニックが必要ですが、これらのテクニックを正しく適用すると、多くの場合、パフォーマンスが大幅に向上します。
レイヤーを設定
階層化アプローチを使用する場合、最初のステップは DOM 上にキャンバスを設定することです。通常、これはキャンバス要素を定義して DOM に配置するだけで簡単ですが、キャンバス レイヤーには追加のスタイル設定が必要になる場合があります。 CSS を使用するときにキャンバス レイヤを正常に実装するには、次の 2 つの要件があります。
各キャンバス要素はビューポート内の同じ位置に共存する必要があります。
各キャンバスは別のキャンバスの下に表示される必要があります。
図 1 は、レイヤー設定の背後にある一般的なオーバーレイの概念を示しています。
図 1. レイヤーの例
レイヤーを設定する手順は次のとおりです:
キャンバスのオーバーラップスタックを設定します
CSS でオーバーレイ スタックを作成するには、少量のスタイル設定が必要な場合があります。 HTML と CSS を使用してオーバーラップする方法は数多くあります。この記事の例では、
可視性の重複を使用して、レイヤー技術の 2 番目のスタイル要件を達成します。この例では、リスト 2 に示すように、このオプションを使用して DOM 要素の背景色を設定します。
リスト 2. 透明な背景を設定するためのスタイルシートのルール
XML/HTML コード
背景が透明になるようにキャンバスのスタイルを設定します。これにより、重なったキャンバスが表示されるという 2 番目の要件が満たされます。レイヤー化のニーズを満たすようにマークアップとスタイルを構造化したので、レイヤー化されたシーンをセットアップできます。
レイヤリングに関する考慮事項
最適化戦略を選択するときは、その戦略を使用する際のすべてのトレードオフを認識する必要があります。 HTML5 キャンバス シーンの階層化は、実行速度の利点を得るために使用される実行時のメモリに重点を置いた戦略です。ページのブラウザにさらに重みを追加して、フレーム レートを高速化できます。一般に、キャンバスはブラウザ上のグラフィックス プレーンとみなされ、グラフィックス API が含まれます。
Google Chrome 19 でテストし、ブラウザのタブのメモリ使用量を記録することで、メモリ使用量の明確な傾向を確認できます。このテストでは、(前のセクションで説明したように) すでにスタイル設定されている
Google Chrome のタスク マネージャーでは、ページで使用されているメモリ (RAM とも呼ばれます) の量を確認できます。 Chrome は、GPU メモリ、または GPU によって使用されるメモリも提供します。これは、ジオメトリ、テクスチャ、またはコンピューターがキャンバス データを画面にプッシュするために必要となるあらゆる形式のキャッシュ データなどの一般的な情報です。メモリが少ないほど、コンピュータにかかる重量が減ります。根拠となる明確な数値はまだありませんが、プログラムが限界を超えてメモリを過剰に使用していないことを確認するために、常にテストする必要があります。メモリの使用量が多すぎると、メモリ リソース不足によりブラウザまたはページがクラッシュします。 GPU 処理は野心的なプログラミングの追求ですが、この記事の範囲を超えています。まずは OpenGL を学ぶか、Chrome のドキュメントを確認することから始めてください (「参考文献」を参照)。
表 1. キャンバス層のメモリオーバーヘッド
表 1 では、より多くの HTML5 キャンバス要素がページに導入され、使用されると、より多くのメモリが使用されます。一般的なメモリにも線形相関がありますが、層が追加されるたびにメモリの増加は大幅に減少します。このテストでは、これらのレイヤーのパフォーマンスへの影響は詳しく説明されていませんが、キャンバスが GPU メモリに重大な影響を与える可能性があることが示されています。プラットフォームの制限によってアプリケーションが実行できなくなることがないように、ターゲット プラットフォームでストレス テストを必ず実行してください。
階層化ソリューションの単一キャンバスのレンダリング サイクルを変更する場合は、メモリ オーバーヘッドに関するパフォーマンスの向上を考慮してください。メモリのコストは高くなりますが、この技術は各フレームで変更されるピクセルの数を減らすことで効果を発揮します。
次のセクションでは、レイヤーを使用してシーンを整理する方法について説明します。
シーンのレイヤー化: ゲーム
このセクションでは、スクロール プラットフォームのランナー スタイル ゲームでの視差効果の単一キャンバス実装をリファクタリングすることにより、多層ソリューションを見ていきます。図 2 は、雲、丘、地面、背景、およびいくつかのインタラクティブなエンティティを含むゲーム ビューの構成を示しています。
図 2. 合成ゲームビュー
ゲームでは、雲、丘、地面、背景はすべて異なる速度で動きます。基本的に、背景の奥にある要素は手前の要素よりも遅く移動するため、視差効果が生じます。状況をさらに複雑にしているのは、背景の動きが 0.5 秒ごとにのみ再レンダリングされるほど遅いことです。
背景は画像であり常に変化するため、通常はすべてのフレームをクリアして画面を再レンダリングするのが良い解決策となります。この場合、背景は 1 秒あたり 2 回しか変化しないため、各フレームを再レンダリングする必要はありません。
現在、ワークスペースが定義されているので、シーンのどの部分を同じレイヤー上に置くかを決定できます。レイヤーを整理したら、レイヤー化のためのさまざまなレンダリング戦略を検討します。まず、リスト 3 に示すように、単一のキャンバスを使用してこのソリューションを実装する方法を検討する必要があります。
リスト 3. 単一キャンバスのレンダリング ループの疑似コード
エンティティのレンダリング方法をより適切に指定するには、2 種類のエンティティ オブジェクトを使用する必要があります。リスト 4 は、使用して調整する 2 つのエンティティを示しています。
リスト 4. レンダリング可能なエンティティの擬似コード
XML/HTML コード
XML/HTML コード
リスト 4 のオブジェクトは、エンティティのイメージ、x、y、幅、高さのインスタンス変数を格納します。これらのオブジェクトは JavaScript 構文に従いますが、簡潔にするために、ターゲット オブジェクトの不完全な疑似コードのみが提供されています。現在、レンダリング アルゴリズムは、ゲーム ループの他の要件に関係なく、キャンバス上で画像をレンダリングすることに非常に貪欲です。
パフォーマンスを向上させるには、パン レンダー呼び出しにより、目的のイメージよりも大きなイメージが出力されることに注意することが重要です。この記事ではこの特定の最適化を無視していますが、使用されるスペースが画像が提供するスペースよりも小さい場合は、必要なパッチのみをレンダリングするようにしてください。
レイヤリングを決定する
単一のキャンバスを使用してこの例を実装する方法がわかったので、このタイプのシーンを改良してレンダリング ループを高速化する方法があるかどうかを見てみましょう。階層化手法を使用するには、エンティティのレンダリングの重複を見つけて、階層化に必要な HTML5 キャンバス要素を特定する必要があります。
再描画領域
重複があるかどうかを判断するには、再描画領域と呼ばれる目に見えない領域を考慮します。再描画領域は、エンティティのイメージを描画するときにキャンバスをクリアする必要がある領域です。再描画領域は、図 3 に示すように、レンダリングされたシーンを完璧にするための最適化手法を見つけることができるため、レンダリング解析にとって重要です。
図 3. 合成ゲーム ビューと再描画領域
図 3 の効果を視覚化するために、シーン内の各エンティティには、ビューポートの幅とエンティティのイメージの高さにわたる再描画領域を表すオーバーレイがあります。シーンは、背景、前景、インタラクションの 3 つのグループに分類できます。シーン内の再ペイントされた領域には、異なる領域を区別するために色付きのオーバーレイが表示されます:
ボールと障害物を除くすべてのオーバーラップでは、再描画領域はビューポートの幅に広がります。これらのエンティティの画像が画面のほぼ全体を占めています。変換要件のため、図 4 に示すように、ビューポートの幅全体がレンダリングされます。ボールと障害物はこのビューポートを通過することが期待されており、エンティティの位置によって定義された独自の領域を持つ場合があります。シーンにレンダリングされたイメージを削除して、再描画された領域だけを残すと、個々のレイヤーを簡単に確認できます。
図 4. 再描画エリア
最初のレイヤーは、互いに重なり合っているさまざまな領域に気づくことができるため、明らかです。ボールと障害物のエリアは丘と地面を覆うため、これらのエンティティはインタラクション レイヤーと呼ばれる 1 つのレイヤーにグループ化できます。ゲーム エンティティのレンダリング順序に従って、インタラクション レイヤーが最上位レイヤーになります。
追加のレイヤーを見つけるもう 1 つの方法は、重ならないようにすべてのエリアを収集することです。ビューポートを占める赤、緑、青の領域は重なり合わず、2 番目のレイヤーである前景を形成します。クラウドとインタラクティブ エンティティの領域は重なりませんが、ボールが赤い領域にジャンプする可能性があるため、このエンティティを別のレイヤーにすることを検討する必要があります。
黒い領域については、背景エンティティが最終レイヤーを構成することが容易に推測できます。このシーンには当てはまりませんが、ビューポート全体を埋める領域 (背景エンティティなど) は、レイヤー全体のその領域を埋めるものとみなされる必要があります。 3 つのレイヤーを定義したら、図 5 に示すように、このレイヤーをキャンバスに割り当て始めることができます。
図 5. 階層化されたゲームビュー
グループ化されたエンティティごとにレイヤーを定義したので、キャンバスのクリアの最適化を開始できます。この最適化の目的は、処理時間を節約することです。これは、各ステップでレンダリングされる画面上のフィクスチャの数を減らすことで達成できます。さまざまな戦略を使用すると、画像がより適切に最適化される可能性があることに注意することが重要です。次のセクションでは、さまざまなエンティティまたはレイヤーの最適化方法を検討します。
レンダリングの最適化
エンティティの最適化は、階層化された戦略の中核です。エンティティを階層化すると、レンダリング戦略を採用できるようになります。通常、最適化手法はオーバーヘッドを排除しようとします。表 1 で述べたように、レイヤーの導入によりメモリのオーバーヘッドが増加しました。ここで説明する最適化テクニックは、ゲームを高速化するためにプロセッサーが実行する必要がある作業量を削減します。私たちの目標は、レンダリングするスペースの量を減らし、各ステップからできるだけ多くのレンダリングとクリーンの呼び出しを削除する方法を見つけることです。
単一エンティティのクリア
最初の最適化はスペースのクリアを目的としており、エンティティを構成する画面のサブセットのみをクリアすることで処理を高速化します。まず、領域の各エンティティの周囲の透明ピクセルと重なる再描画領域の量を減らします。この手法を使用すると、ビューポートの小さな領域を満たす比較的小さなエンティティが含まれます。
最初のターゲットはボールと障害物です。単一エンティティのクリア手法には、エンティティを新しい位置にレンダリングする前に、前のフレームでエンティティがレンダリングされた位置をクリアすることが含まれます。各エンティティのレンダリングにクリーンアップ ステップを導入し、エンティティの画像の境界ボックスを保存します。このステップを追加すると、リスト 5 に示すように、エンティティ オブジェクトが変更されてクリーンアップ ステップが組み込まれます。
リスト 5. 単一ボックスのクリアリングを含むエンティティ
更新ステップの前に呼び出される各エンティティのクリア メソッドを作成することで、このレンダリング ソリューションを実装できます (ただし、この記事ではクリア メソッドは使用しません)。リスト 6 に示すように、このクリアリング戦略を PanningEntity に導入して、地上および雲のエンティティにクリアリングを追加することもできます。
リスト 6. 単一ボックスをクリアする PanningEntity
XML/HTML コード
PanningEntity はビューポート全体に及ぶため、キャンバスの幅を四角形のサイズとして使用できます。このクリア戦略を使用すると、雲、丘、および地上エンティティに対して定義された再描画エリアが与えられます。
クラウド エンティティをさらに最適化するために、クラウドを独自の再描画領域を持つ個別のエンティティに分離できます。そうすることで、クラウドの再描画領域内でクリアする必要のある画面スペースの量が大幅に減少します。図 7 は、新しい再描画領域を示しています。
図 7. 個別の再描画領域を持つクラウド
単一エンティティのクリア戦略は、このゲームのような階層化されたキャンバス ゲームのほとんどの問題を解決するソリューションを生成しますが、それでも最適化することができます。このレンダリング戦略の極端なケースを見つけるために、ボールが三角形と衝突すると仮定します。 2 つのエンティティが衝突すると、エンティティの再描画領域が重なり、望ましくないレンダリング アーティファクトが作成される可能性があります。もう 1 つのクリーンアップの最適化は、衝突する可能性のあるエンティティにより適しており、レイヤリングにも役立ちます。
ダーティ長方形クリア
単一のクリーニング戦略がない場合は、ダーティ長方形のクリーニング戦略が強力な代替手段となる可能性があります。このクリア戦略は、高密度粒子システムや小惑星を使用した宇宙ゲームなど、領域が再描画された大きなエンティティで使用できます。
概念的には、アルゴリズムは、アルゴリズムによって管理されるすべてのエンティティの再描画領域を収集し、1 回のクリア呼び出しで領域全体をクリアします。さらなる最適化のために、このクリーンアップ戦略では、リスト 7 に示すように、各独立したエンティティによって行われた重複したクリーンアップ呼び出しも削除されます。
リスト 7.DirtyRectManager
シェイプ コンピューティングがマネージャに追加され、これにより、マネージャがクリア時にクリア 7 内で計算できるようになります。
図 8. 対話層の再構成領域
図8は、対話層のオブジェクトに対するアルゴリズムによって計算された再実行領域を示しています。 >清除としての重写
前に染まった画像を上に塗り直すことによって、背景などの最大のレイヤーを追加することもできます。
各層の重み領域を減らすことによって、層とその層に含まれるオブジェクトが効果的に実現されています。ペイントの分層化は、すべての対話タイム フィールドに適用できる戦略の 1 つです。重複した再構成領域の集合があり、層を指定できるため、パーティクルシステムまたは大量の物理オブジェクトを同時に必要とする場合、画層を分割するのに最適な選択肢となる可能性があります。