Unity UI の基礎

確認済のバージョン: 5.3

-

難易度: Advanced

Unity UI システムを理解するには、それを構成する各要素について理解することが大切です。このシステムは、いくつかの基礎的なクラスとコンポーネントが組み合わさって構成されています。この章では、このガイドの中で使用される用語の解説に続き、Unity UIの重要な仕組みのいくつかの、低水準挙動について解説します。

用語解説

Canvas は、Unityのネイティブコードのコンポーネントのひとつです。ゲームのワールド空間の中、あるいは上に描かれるレイヤー状のジオメトリをレンダーするためにUnityが使用しているコンポーネントです。

Canvas は、その構成要素であるジオメトリを組み合わせてバッチを作成し、適切なレンダリングコマンドを生成し、そのコマンドをUnityのGraphicsシステムに送る役目を持っています。この全てがネイティブのC++コードで行われ、このプロセスはリバッチまたはバッチビルドと呼ばれています。「リバッチを要するジオメトリを含んでいる」とマークされたCanvasは、ダーティであると見なされます。

ジオメトリ(Geometry)はCanvas RendererコンポーネントによってCanvasに提供されます。

Sub-canvas とは、別のCanvasコンポーネントの中に入れ子になっているCanvasコンポーネントのことです。Sub-canvasは、その子を親から隔離します。つまり、ダーティな子によって親がジオメトリの再ビルドを強制されることも、その逆もありません(1)

Graphic は、Unity UIのC#ライブラリが提供するベースクラスのひとつです。描画可能なジオメトリをCanvasシステムに提供する全てのUnity UI C#クラスのベースクラスです。ビルトインのUnity UI GraphicのほとんどはMaskableGraphicサブクラスによって実装されているため、IMaskableインターフェースによってマスキングが可能です。Drawableの主要なサブクラスはImageとTextで、それぞれ、ImageコンポーネントとTextコンポーネントを提供します。

Layout コンポーネントはRectTransformsのサイズと位置を制御します。通常は、コンテンツの相対的なサイズ設定や相対的位置の設定を要する複雑なレイアウトの作成に使用されます。LayoutコンポーネントはRectTransformsに依存しており、関連付けられたRectTransformsのプロパティにのみ作用します。Graphicクラスには依存しておらず、Unity UIのGraphicコンポーネントから独立して使用することができます。

Graphic コンポーネントとLayoutコンポーネントは両方ともCanvasUpdateRegistryクラスに依存しています。CanvasUpdateRegistryクラス はUnityエディターのインターフェースからは利用できません。このクラスは、更新が必要なLayoutおよびGraphicコンポーネントを記録し、それらに関連付けられたCanvasがwillRenderCanvasesイベントを呼び出した時に随時更新をトリガーします。

Layout およびGraphicコンポーネントの更新はリビルドと呼ばれます。リビルド処理についても、このガイドの中で詳しく解説しています。

レンダリングの詳細

Unity UI でユーザーインターフェースを構築するに当たっては、Canvasによって描かれる全てのジオメトリがTransparentキューで描かれるということを理解する必要があります。つまり、Unity UIによって生成されたジオメトリは常にアルファブレンディングを用いて背面側から先に描画されるということです。パフォーマンスの面から見て重要なのは、ポリゴンからラスタライズされたピクセルは、別の不透明ポリゴンによって完全に被われている場合も含め、全て抽出されるということです。モバイルデバイスでは、この大量のオーバードローによってすぐにGPUフィルレートの許容範囲を超えてしまう可能性があります。

バッチビルドのプロセス(Canvas)

バッチビルドの処理とは、CanvasがそのUI要素を表すメッシュを組み合わせ、Unityのグラフィックス・パイプラインに送るための適切なレンダリングコマンドを生成する処理のことです。この処理のリザルトはキャッシュされ、Canvasがダーティであるとマーキングされるまで再利用されます。Canvasがダーティであるとマーキングされるのは、それを構成しているメッシュのどれかに変更が加わった時です。

Canvas が使用するメッシュは、そのCanvasに付属した一式のCanvas Rendererコンポーネントの中にあり、かつどのSub-canvasにも含まれないものから選ばれます。

バッチを算出するには、メッシュを深度によってソートし、その重なりや共有マテリアルなどに関した検査を行う必要があります。この操作はマルチスレッドで行われ、したがってそのパフォーマンスは通常、CPUのアーキテクチャによって大きく異なります。特に(基本的にCPUコアの数が少ない)モバイルSoCの場合と、(コアを4つ以上持っている場合が多い)最近のデスクトップCPUの場合とでは、顕著に異なります。

リビルド処理(Graphics)

リビルド処理では、Unity UIのC# Graphicコンポーネントのレイアウトとメッシュが再計算されます。この処理はCanvasUpdateRegistryクラスで実行されます。これはC#クラスであり、ソースはUnityのBitbucketにあります。

CanvasUpdateRegistry 内では、メソッドはPerformUpdateになります。このメソッドは、CanvasコンポーネントがイベントWillRenderCanvasesを起動すると起動されます。このイベントはフレームごとに1回呼び出されます。

PerformUpdate の処理は次の3段階で行われます。

  • ICanvasElement.Rebuild メソッドによって、ダーティなLayoutコンポーネントに、そのレイアウトのリビルドがリクエストされる。
  • 全ての登録された Clippingコンポーネント([例]Masksなど)に、クリップされた全てのコンポーネントのカリングがリクエストされる。これはClippingRegistry.Cullによって行われます。
  • ダーティなGraphicコンポーネントに、そのグラフィック要素のリビルドがリクエストされる。

Layout およびGraphicのリビルドの処理は、複数の部分に分かれています。Layoutリビルドは3つの部分(PreLayout、Layout、PostLayout)に分けて実行され、Graphicリビルドは2つの部分(PreRenderとLatePreRender)に分けて実行されます。

Layoutのリビルド

1つ以上のLayoutコンポーネントに含まれるコンポーネントの適正な位置(または位置とサイズ)を再計算するには、Layoutを適正な階層的順序で適用する必要があります。GameObjectヒエラルキーのルートに近いLayoutは、その中に入っているLayoutのサイズに変更を及ぼす可能性があるため、最初に計算される必要があります。

これを行うにあたり Unity UIは、ダーティなLayoutコンポーネントのリストを、各コンポーネントのヒエラルキー内における深度によってソートします。ヒエラルキーの上部にある(つまり親Transformの数が少ない)アイテムがリストの先頭に移動されます。

Layout コンポーネントのリストがソートされると、そのリストに対してレイアウトのリビルドがリクエストされます。このタイミングで、Layoutコンポーネントによって制御されるUI要素の位置とサイズが実際に変更されます。Layoutがそれぞれの要素に及ぼす影響についての詳細は、Unityマニュアルの『UI』の『Auto Layout』のページをご参照ください。

Graphic リビルド

Graphic コンポーネントがリビルドされると、Unity UIはICanvasElementインターフェースのRebuildメソッドに制御権を渡します。Graphicはこれを実装し、Rebuild処理のPreRenderの段階で、2パターンのリビルド処理を実行します。

  • 頂点データがダーティとしてマーキングされると、メッシュがリビルドされます。(例.コンポーネントの RectTransformのサイズが変更された場合など)
  • マテリアルデータがダーティとしてマーキングされると、それに付属したCanvas Rendererのマテリアルが更新されます。(例.コンポーネントのマテリアルやテクスチャが変更された場合など)

Graphic Rebuild は決まった順序でGraphicコンポーネントのリストを処理する訳ではなく、ソート操作も一切必要としません。

脚注

  1. これが当てはまらないエッジケースも存在します。例えば、親Canvasに加えられた変更によって子Canvasのサイズが変更される場合などです。