TL;DR: Layout Elementを画像側につけて、必要なサイズを定義→親にLayout GroupとContent Size Fitter
Layout Elementはそれ自体は何もしないが、Layout Groupと組み合わさった時に裏で発動し、
Layout Groupが利用するサイズ情報を上書いてくれるようになる。
読めば書いてあることだが、意外と気づかない機能だったので共有。
それぞれのプロパティの意味は
- Min: 絶対にこれより小さくなって欲しくない大きさ
- Preferred: 無理でない限り指定したい大きさ
- Flexible: 余裕があった時に埋めたい大きさを、親要素の大きさに対する割合で指定
Preferred
で強制的に画像側のサイズを決定したら、親要素にContent Size Fitter
を入れてPreferred Size
を適用すれば、問題なく表示される。ラベルにElementを追加する必要はない。
要素の大きさが違うだけで困った
このように「キーボードのキートップの絵(Raw Image)」と「そのキーが何をするかを書いたラベル(TMPUGUI)」を組み合わせて操作説明のUIを作ることを考える。(生憎購入したキートップアセットがTexture
になっていたためRaw Image
を利用しているが、Image
でも同じ問題が起こりうる)
ひとまず、キートップとラベルを親要素でまとめて作ってみよう。
この時操作説明の文章の文量が変わらないのなら静的に作ってしまってもいいが、例えば他言語対応するとなると、それぞれの言語でテキストの幅が変わってしまって微妙な隙間ができて悲しいことになる。
翻訳した結果が元の文字列の幅より長かったらもっと悲惨だ(日本語を先に作った時など)。
これは流石に自動化したいところ。
Horizontal Layout Groupが荒ぶる
こういう時こそと思ってHorizontal Layout Groupを親GameObjectに適用してみたら、余計に悲しいことになった。キートップが消滅したのである。
ラベルも全く正しいサイズになっていない。
Layout Element + Content Size Fitterが正解
Layout ElementとContent Size Fitterが今回の正解だ。特にLayout Elementはこれ単体では何もしないように見えるコンポーネントなので、先にLayout Groupを見つけてしまった場合は特にElementの存在に気づかないかもしれない。
キーボードのキートップの絵は変えなくていい。ということは、幅も変わらない。従って、絵の方には Preferred Width
が定義できる。合わせて、Height
の方も念のため入れておこう(なぜMin
にしないのかはあとで解説)。
ラベルの方はそのままでいい。この場合は、「そのGameObjectについている描画系コンポーネント(この場合TextやTMPUGUI)が報告する値をそのまま使う」という動作になる。 Inspectorの一番下に「Layout Properties」というタブが隠れていて、上にドラッグすると見えるようになる。ここに書かれている値が親側で使われる値となる(Source
がTextMeshProUGUI
であることに注目)。
最後に、親要素に対して、Layout Group(今回は Horizontal Layout Group
)を入れ、Control Child Size
を Width
に関して有効にする。その上で、Content Size Fitterも同時に適用して Horizontal Fit
を Preferred Size
にする。こうすることで、Elementを入れただけで放置していたラベルが裏で計算してLayout Groupに伝えていたPreferred Width
を親要素に対して適用してくれる。
ちなみに:
- Horizontal Layout Groupの
Contorol Child Size
→Width
が付いてない場合
→ラベル要素が伸び縮みしない - Content Size Fitterを使わない場合や、
Horizontal Fit
をUnconstrained
やMin Size
にした場合
→親要素が伸び縮みしないので、UnityのシーンEdit時に定義したより長いラベルになったときに変な改行ができてしまう
→Min Sizeにした場合、キートップは消滅しラベルの文字が縦書きになる(後述)
という不具合が起こる。
そもそも何が問題だったのか?
先ほど少し紹介した Layout Properties
、ここに答えが隠れていた。Layout Elementを使わない場合のPropertiesの数値は以下の通りになっていた。
Preferredのサイズ | |
---|---|
Image |
Source Image に指定しているSprite の元のサイズ |
Raw Image | 0 x 0 |
ラベル(TMPUGUI) | ラベルの内容が指定された通りに表示できる最小サイズ |
(Minは全て0 x 0、Flexibleは全てdisabled)
実は、これらがLayout Group系のコンポーネントがレイアウトを計算するときに見ている数字である。
Content Size Fitterは小要素の(報告した)Min Size
ないしは Preferred Size
を見ることでLayout Groupを持っている要素のサイズを計算することができる。だが、このままだとHorizontal Layout Groupを入れてContent Size Fitterを使ったとしても、全てを一発で適切なサイズに合わせられる設定がない。Minだと全て 0 x 0 になってしまうし、Preferredなら Imageは原寸、Raw Imageは潰れてしまう。
そこで、Text系のPreferred
が自動計算されていることを利用して、ImageコンポーネントにLayout ElementコンポーネントをつけてこのGameObjectがContent Size Fitterに報告するPreferred
を上書きし、FitterにPreferred
の値を使ってサイズ変更させるようにすることで、自動で長さを変える機能を実現したわけである。
Content Size Fitterの設定を Min Size
にしたときにおかしな見た目になる理由も、これで説明がつくだろう。全てが0 x 0になっているのだ。上書きできるとはいえ、せっかくPreferred Sizeが自動計算されるラベル系コンポーネントの旨味が活かせない。
Layoutで困ったら、このLayout Propertiesを覗いて誰がなんと報告しているのかを見るのが一番近道かもしれない。
ちなみに
この辺の説明は次のサイトがわかりやすく解説しています: