【Unity】uGUIの自動レイアウトが分かりにくいと評判なので解説してみる
uGUIには自動レイアウトなシステムが積まれています。ただ、このシステム便利ではあるんですが色々と複雑なので、少し補足です。
UIの大きさを動的にフィットさせる、オートレイアウト
UIを構築する上では、必ずしも画面や要素の大きさが一致とは限りません。画面で言えば16:9の場合もありますし9:16の場合もあります。ソレ以前に4:3の場合もあります。
また、画面だけでなく子の要素も必ずしも固定とは限りません。分かりやすいのがフキダシのような項目で、文字の量によって大きさが違います。
単純なレイアウトの調整の場合は9スライスやアンカーで十分レイアウトの対応が可能ですが、そこに要素の変更まで加わると、ややこしい事になります。
このフレキシブルなスケールに対応するのがオートレイアウトなシステムの便利な所で、上手く使えばコードを書かずにうまい感じにスケールを調整してくれます。
※簡単では無いですし、シンプルになる訳でも無い点に注意
UIの大きさはLayoutElementで決まる
オートレイアウトシステムの各要素のサイズは、LayoutElementに依存します。つまり、ほぼRectTransformで動作しません。むしろLayoutElementの値に従いRectTransformの値を書き換えます。pivotは除く
実際Auto Layoutの機能を使用すると、RectTransformで操作できなくなる項目が出現します。具体t系にはPosition・Width/Height・Anchorsは動かない物と思った方が良いです。
ここで使用するパラメータは大きく分けて3つで、Min(Width/Heiht)、Flexible(Width/Height)、Perferred(Width/Height)です。
またLayoutElementを使用しない場合も、ILayoutElementを継承しているコンポーネントの場合、サイズを持つ事があります。例えばTextとか、Image。
なお基本的にuGUIのImageやTextはminの値を持ちません。LayoutElementを設定するか、LayoutGroupを通すと設定される項目みたいです。
Minは最低限サイズ、Preferredは変化するサイズ、Flexibleは余りを割合で
- Min□
これ以上小さくならない幅・高さ(サイズ)
最低限描画される事が保証される範囲 - Preferred■
描画出来る幅・高さ(サイズ)があれば描画する - Flexible■
RectTransformのサイズからMinもしくはPerferredを引いた割合
例えば下のようにUIのサイズを変更すると、白(Min)は最後まで変化が無いですが、青(Flexible)が縮んだり伸びたりします。青が無くなると赤が縮み始めます。
ここで混乱しやすいのが、MinとPreferredはサイズなのに対しFlexibleは割合であるという点です。
例えば下の図では、上は左右の端をPreferredで、下をFlexible Widthで設定しています。Perferredでは20ピクセルで描画されているのに対し、Flexibleの場合は14%で描画されています。*1
minとpreferredとflexbleの全てが設定されてる場合、採用されるのは一番大きなサイズみたいです。
レイアウトプロパティの確認はInspectorビューで
LayoutPropertyの確認は、Inspectorビューのプレビューから確認出来ます。Valueでどの程度の値か、Sourceでレイアウトのサイズを決めているのが誰かを確認します。
UIを並べるだけでなくサイズも変更するLayoutGroup
各UI要素のサイズが確定したら、LayoutGroupでUIを並べます。これ(自分も含めて)レイアウトを並べる機能として紹介されていますが、実際にはレイアウトのサイズを変更して、並べる機能です。*2
ここからがややっこしい内容です。覚悟はいいか?俺はできてる。
LayoutGroupは子のLayoutを親に伝える
LayoutGroupは子のレイアウトサイズを保持し親に伝える役割もあります。
逆を言えば、LayoutGroup(もしくはソレに類するもの)がLayoutEmelentを宣言しない場合、親にLayoutElementの要素は伝わりません。
つまり、オートレイアウトな機能を使用する場合、UIの親は必ず何らかのLayoutGroupを保持している必要があります。でないとレイアウトが破綻します。
例えば下に二つの例を用意します。この二つの違いは、選択中のUI(Image)にLayoutが設定されているかどうかです。
上の画像は中央のImageにレイアウトが設定されているため、子のImageサイズを取得して親に伝えているため、最終的にUIが必要としているサイズは128です。
逆に、下の画像の場合、中央にLayoutGroupが無く子のPreferredなサイズを取得していないため、最終的なサイズは0になってしまっています。
ちな正確には「伝える」ではなく「親が勝手に見る」です
Control Child Sizeは子のLayoutを元に子のRectTransformを変更する
まずControl Child SizeはLayougGroupの子の要素のサイズを変更する機能です。正確には、一つ下の子が提出してきたサイズを元に子のサイズを決める機能を有効にする機能です。
最もコレをよく使うのが、テキストの内容を変更したケースで、文字数や文字幅に応じてウィンドウのサイズを動的に変更する事を期待出来ます。例えば下のようなケース。
- Textが文字列を元にPreferredの高さと幅を算出
- WindowのレイアウトがTextの高さと幅を取得
Control Child Sizeを元にTextのWidthとHeightを更新 - ContentがWindowのpreferred widthとpreferred heightを元にwidthとheightを更新
Control Child SizeがFalseの場合、RectTransformのサイズをLayoutに使う
またややっこしいのが、control child sizeをfalseに設定していると、RectTransformのサイズをlayout のサイズ(しかもmin)に使用する点です。
例えば下の場合、WindowのControl child sizeは設定されておらず、RectTransformの値が使用されています。つまり下のような流れです。
- Windowは子のRectTransformのサイズを元に、Layoutを設定
- ContentはWindowのLayoutを元にWindowのWidthとHeightを設定
並べる際、設定したサイズをそのまま使える為、単純なリスト構造を作るならコチラの方が有用かもしれません。
Control Child Sizeによるリサイズは親より大きくならない
minが設定されている場合は別ですが、基本的にLayoutGroupで駆動するUIの大きさは一番上のLayoutGroupより大きくなりません。折り返しや省略等は親のRectTransformに従って行われます。
とは言え、それでは困るケースもあります。例えばScrollView等で子要素を元に親オブジェクトを大きくしたいケースです。
ここでContent Size Fitterが登場します。
設定したオブジェクトのサイズをminもしくはPreferredのサイズに従って変更します。使えば、テキストで広がった分だけRectTrasnformが広がります。
逆を言えば、最後に親オブジェクトのサイズを変更したい場合以外では、Content Size FitterではなくLayoutGroupでなんとかしろというお話でもあります。
Child Force ExpandはUIを引き伸ばす
Child Force Expandは説明文の通りです。
追加の利用可能なスペースを埋めるため子要素を強制的に拡大するか
レイアウトの幅が余っている場合、埋めるように拡大します。自身と子を。
この項目は初期値でEnableですが、基本的にDisableにしたほうが良い項目じゃないかなと個人的には思います。この項目Undo効きませんし
Vertical LayoutかHorizontal Layoutのどちらを使うべきか?…好みで
さんざんLayoutGroupと行ってきましたが、Grid Vertical Horizontalの3種類のうちどれを使えば良いかと言うと、お好みです。整形のルールは異なりますが、LayoutGroupの機能として見れば同じものなので、好みでどちらを使うべきか決めても良いかと。
LayoutGroupだけの機能があれば健全なんですが…