#26 の実装にあたって、少しLayoutについて調べた内容を備忘のためにまとめます。
Layout とは
Layout は整列機能です。親ゲームオブジェクトにぶら下げた子オブジェクトを、親オブジェクトが「ある規則性」に沿って整列させる機能です。「ある規則性」には、標準では「横一列」(Horizontal)、「縦一列」(Vertical)、「格子状」(Grid)の3種類あります。「整列する」とは、整列される要素それぞれの位置を決めて配置することです。親のサイズに収めるように並べるのか、親のサイズは関係ないのか、まずそれが気になると思います。
Layout を構成するインターフェース
Layout を構成するインターフェースは ILayoutElement(整列される側) と ILayoutController(整列する側)に分かれます。上記の3つのコンポーネント(Horizontal、Vertical、Grid)とこの2つのインターフェースは以下のように関連しています。
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 だけが登録されます。
LayoutElement の取り出し
ILayoutElement は、CanvasUpdateRegistry の更新タイミングでキューから取り出されます。正確には、ILayoutElement が属している最もルートの ILayoutGroup がキューから取り出されます。ILayoutElement は、この ILayoutGroup を実装したコンポーネントからサイズを参照されたり、サイズ計算を要求されたりします。ここでいうサイズは、RectTransform の width/heightではなく、ILayoutElement として持っている min, preferred, flexible という3つのサイズです。
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 を更新します。
RectTransform の更新(位置の決定、および拡大縮小)
ILayoutGroup は、その後 LayoutRebuilder から、子の RectTransform の並べ替え、および大きさの決定を要求されます。この時コールされるのが、SetLayoutHorizontal/Vertical です。ILayoutGroup は、子の RectTransform情報を更新し、必要に応じて 子のサイズ を変更します。
整列動作(GridLayoutGroupの場合)
VerticalLayoutGroup の サイズ計算と整列の処理は、GridLayoutGroup と比較するとより鮮明になります。GridLayoutGroup は Horizontal と Vertical の両方を持っている印象ですが、シーケンスはもう少しシンプルです。
min, preferred, flexible サイズの計算
RectTransform の更新(位置の決定、および拡大縮小)
独自の整列機能を作る場合、GridLayoutGroup → LayoutGroup の使い方は参考にできそうです。
(以上)