ゲーム化!tomo_manaのブログ

ゲーム化!tomo-manaのブログ

Unityでゲームを作る方法について紹介しています

Unity学習#26-1 Layout (Unity 2019.4.4f1)

#26 の実装にあたって、少しLayoutについて調べた内容を備忘のためにまとめます。

Layout とは

Layout は整列機能です。親ゲームオブジェクトにぶら下げた子オブジェクトを、親オブジェクトが「ある規則性」に沿って整列させる機能です。「ある規則性」には、標準では「横一列」(Horizontal)、「縦一列」(Vertical)、「格子状」(Grid)の3種類あります。「整列する」とは、整列される要素それぞれの位置を決めて配置することです。親のサイズに収めるように並べるのか、親のサイズは関係ないのか、まずそれが気になると思います。

Layout を構成するインターフェース

Layout を構成するインターフェースは ILayoutElement(整列される側) と ILayoutController(整列する側)に分かれます。上記の3つのコンポーネント(Horizontal、Vertical、Grid)とこの2つのインターフェースは以下のように関連しています。

f:id:tomo_mana:20210128224119p:plain
Layout - Unity

ILayoutElement

ILayoutElement(整列される側) のインターフェースは以下です。

関数名 説明
CalculateLayoutInputHorizontal() min, preferred, flexible widthを計算する
CalculateLayoutInputVertical() min, preferred, flexible heightを計算する

ILayoutController

ILayoutController(整列する側) のインターフェースは以下です。

関数名 説明
SetLayoutHorizontal() 子階層のRectTransformのwidth, anchorを変更する
SetLayoutVertical() 子階層のRectTransformのheight, anchorを変更する

ILayoutGroup/ILayoutSelfController

ILayoutGroup は子を整列させる目的、ILayoutSelfController は自身を整列させる目的?と考えられます。ILayoutSelfControllerはイメージが湧きにくいので、別の記事でまとめます。ILayoutGroup、ILayoutSelfController はいずれも ILayoutController を継承(実装)したもので、ILayoutController に定義された以上の変数や関数は持っていません。

ILayoutGroup と ILayoutElement 自体はインターフェースなので、これだけだと動きが分かりません。Horizontal・VerticalLayoutGroup、GridLayoutGroupから、これら2つのインターフェースの使われ方を確認します。

整列動作(VerticalLayoutGroupの例)

整列動作には、以下の4つのステップがあります。
(1) LayoutElement の登録
(2) LayoutElement の取り出し
(3) LayoutElement の min, preferred, flexible サイズの計算
(4) LayoutElement の RectTransform を更新

まだ理解していない部分もありますが、それぞれの動きをシーケンス図でまとめます。

LayoutElementの登録

ILayoutElement を持ったゲームオブジェクトは、Unityからのイベント(OnEnableなど)を受け取った時、レイアウトを更新するキューに自分を登録します。ILayoutElement は、LayoutRebuilderクラスを経由して CanvasUpdateRegistryクラス に登録します。通常は、この役目はLayoutElement、またはLayoutGroupコンポーネントが代行するため、コンポーネントを利用する開発者は、登録を意識する必要がありません。尚、この登録はILayoutElement 毎に行われますが、CanvasUpdateRegistry に登録されるのは ILayoutElement が属する ILayoutGroup、それも最もルートに位置する ILayoutGroup だけが登録されます。

f:id:tomo_mana:20210201225629p:plain
LayoutElementの登録 - Unity

LayoutElement の取り出し

ILayoutElement は、CanvasUpdateRegistry の更新タイミングでキューから取り出されます。正確には、ILayoutElement が属している最もルートの ILayoutGroup がキューから取り出されます。ILayoutElement は、この ILayoutGroup を実装したコンポーネントからサイズを参照されたり、サイズ計算を要求されたりします。ここでいうサイズは、RectTransform の width/heightではなく、ILayoutElement として持っている min, preferred, flexible という3つのサイズです。

f:id:tomo_mana:20210131225528p:plain
LayoutElementの取り出し - Unity

min, preferred, flexible サイズの計算

LayoutElement が持っている min, preferred, flexible を簡単に書くと、LayoutElement の所属するゲームオブジェクトが持っている RectTransform の最小サイズ、推奨サイズ、拡大比率です。ILayoutGroup は、設定によっては、ILayoutElement の位置情報(整列)だけでなく大きさを変更する(拡大縮小)ことがあります。

名称 説明
min ILayoutElementの最小サイズ
preferred ILayoutElementが推奨するサイズ。ILayoutGroup が、ILayoutElement の preferred サイズより小さいサイズになることを要求する場合、minサイズまではILayoutElement のサイズを小さくすることを要求できます。
flexible ILayoutElementが許容するサイズ。ILayoutGroup が、ILayoutElement の preferred サイズより大きいサイズになることを要求する場合、flexible を使用します。

ILayoutGroup は、ILayoutElement の位置情報を決定する前に、ILayoutElement の min, preferred, flexible に更新がないかを確認します。その時に呼ぶのが CalculateLayoutInputHorizontal/Vertical です。通常、ILayoutElement だけを使えるようにした LayoutGroupコンポーネントは、この関数がコールされても何もしません。LayoutGroup は、自分が持つすべての子の min, preferred, flexible サイズを計算し、自分の min, preferred, flexible を更新します。

f:id:tomo_mana:20210131230529p:plain
LayoutElementサイズ計算 - Unity

RectTransform の更新(位置の決定、および拡大縮小)

ILayoutGroup は、その後 LayoutRebuilder から、子の RectTransform の並べ替え、および大きさの決定を要求されます。この時コールされるのが、SetLayoutHorizontal/Vertical です。ILayoutGroup は、子の RectTransform情報を更新し、必要に応じて 子のサイズ を変更します。

f:id:tomo_mana:20210131225838p:plain
LayoutElement整列 - Unity

整列動作(GridLayoutGroupの場合)

VerticalLayoutGroup の サイズ計算と整列の処理は、GridLayoutGroup と比較するとより鮮明になります。GridLayoutGroup は Horizontal と Vertical の両方を持っている印象ですが、シーケンスはもう少しシンプルです。

min, preferred, flexible サイズの計算

f:id:tomo_mana:20210131225626p:plain
GridLayoutGroupサイズ計算 - Unity

RectTransform の更新(位置の決定、および拡大縮小)

f:id:tomo_mana:20210202001837p:plain
GridLayoutGroup整列 - Unity

独自の整列機能を作る場合、GridLayoutGroup → LayoutGroup の使い方は参考にできそうです。

(以上)