By analysing and modifying the data types involved in other games’ mechanics, one can generate interesting new ideas and train one’s ability to “break” conventions. It can also shed light on *why* certain choices are convention in the first place.
Are you confused about Unity’s Layout Groups? Specifically the mysterious “Child Controls Size” checkboxes? Then this article is for you!
TL;DR: Unity’s Auto Layout system is confusing because it uses parameters to compute its layout that by default are hidden and can only be overridden by manually adding the “Layout Element” component to child ui elements. Also, Flexible Width/Height are not measured in units, but as ratio weights, and are applied on the unused space that is remaining after all preferred/min heights have been subtracted.
Working with Layout Groups in Unity, upon first (or second or third) encounter can be confusing, even frustrating at times. This is even stranger due to the fact that the rest of Unity’s UI system is very easy to use and understand. And since for Puzzle Pelago, I needed to be able to support various aspect ratios and orientations, I had to understand this system in detail. Systematically looking at isolated fields and Checkboxes, and reading the official documentation on this multiple times, I think I unwound my knot, and hopefully yours, too!
If you only care about the highly condensed learnings, just look for the “What we learn” sections.
In what follows, I will do a step by step exploration of Unity’s Vertical Layout Groups. Horizontal Layout Groups work analogously and are omitted here for brevity.
To start out, let’s create a new scene in any Unity project, and set the game view’s resolution to a custom 300x400 resolution. This will make it easier later to see how unity calculates layouts. Once this is set up, we can add a new Canvas, and to that Canvas, add a “Vertical Layout Group” component Lets also set both checkmarks for the Width-aspect of the layout. We are only focusing on the vertical aspects here, and so we will initially uncheck both Height-related checkmarks.
Now add three UI images into that canvas, and change only their colors to be able to tell them apart. I chose white, green and red.
This is what the scene now looks like:
The images have been stretched to width 300 to fit the whole canvas, since “Force Expand” is enabled for width. The height for each image is exactly 100, since that is the default height for images, and due to both checkmarks unchecked in the layout group for height, this was left untouched.
With this basic setup, lets go and try out some things.
In the Vertical Layout Group on the Canvas, tick “Child Controls Size” for Height.
All three images are gone now! What the hell?? (Explanation on that later)
I would have expected, since the checkbox says “Child Controls Size”, that the height value would remain as is. But nope. Child Controls Size => height=0. Hmmmm.
If you try and change the image’s height, you can’t! The controls are disabled. Very confusing! I thought the child controls its own size?
Tick “Child Force Expand” for Height.
The images are back and now fill out the whole canvas. Makes more sense.
Untick “Child Controls Size” for Height.
Nothing changes, really.
But, now we can go into the images and manually control their height again. At least something!
Re-tick “Child Controls Size” for Height.
Back to full screen usage.
What we learn
Setting “Controls Size” without “Force Expand” leads to height = 0
As explained in the docs, this is because the default preferred, minimal and flexible heights are all 0.
So what this means is that “Child Controls Size” actually means that hidden, by default implicit layout parameters of the child “Control Size” of the layout elements, and not the explicit height values! Since these hidden, default values are all 0, our images vanished once we let them control their height.
Now let’s say we want a fixed (i.e. “preferred”) height of 100 for the top header row, and below that, two flexible height containers, of which the first one is twice as large as the bottom one.
For each image, in order, add a “Layout Element” Component, which allows us to override the default Auto Layout behaviours / measurements
For the first one, tick “Preferred Height” and enter 100
For the second and third one, tick “Flexible Height"
For the first one, enter 2
For the second one enter 1
Make sure that on the Canvas’ Layout Group, “Force Expand” is off for height, such that only “Child Controls Size” is on.
What we learn
Flexible Height parameters are not actually units, but weight values, of which ratios between the layout elements will be calculated to automatically calculate their relative use of space.
Since by default, Flexible height for the 1st image is 0, and the preferred size is set at 100, it means that the remaining space will be divided between the second and third element at 2:1 ratio!
From the docs: "If there is additional available space, flexible size is allocated."
Ok. But now, just out of curiosity. What happens if for the top image, we also tick “Flexible Height” and set its value to 1? I would expect that now the element height ratios are 1:2:1, i.e. the top element takes up exactly 100 units since it’s share of the height is 1/4*400 units, which is both its preferred and its flexible height.
But Nope! The height becomes 175. The math here seems to be as follows:
Top: 100 units preferred, leaves 300 units below empty; + weight of 1 to take from empty space (300/4 = 75; 100+75 = 175 observed height)
Middle: weight of 2, i.e. 300/2 = 150 (as expected)
Bottom: weight of 1, i.e. 300/5 = 75 (as expected)
What we learn
The “flexible height” ratios apply after the min/preferred heights have been subtracted, if they are set, i.e. absolute and relative heights are additive!
So, lets have a look at our layout with the iPhone 5 16:9 resolutions in both portrait (i.e. 9:16) and landscape. We can switch between both screen orientation to check how our layout looks in both.
Lets say we want to use the bottom red portion for next / back buttons. In portrait, it could be convenient to expand this to have a size about 2x the header (i.e. 200 units). This gives the buttons some room to breathe and should be reachable more easily than cramming them in the bottom 100 units.
If we solve this by setting the Min Height to 200, this will look horrible when going to landscape orientation, since in landscape, 200 is way too much!
Turns out, what we want to do here is set the min height to 100, which is a value that looks good in landscape mode as well, and then also use the flexible height value of 1.
In addition, we must now tell the middle part to use flexible height, with a large value, like 8, to get it expand more than the bottom part.
By using both flexible and min height, we get to ensure a minimum height, but also have control over how the ratios between heights change in different aspect ratios.
It is a bit frustrating that we cannot use the flexible and min height together to create a region that e.g. uses 25% of the available height, but is also at minimum 100 units high. But alas we will have to use workarounds like the above.
I hope this has helped resolve confusion about AutoLayout and (Vertical) Layout Groups in Unity’s UI system a little bit. If there are more questions, please don’t hesitate to contact me via twitter @hallgrimgames or in the comments section below.