1
\$\begingroup\$

I feel sure that I'm missing something obvious, but I've been stuck with this over the past few days.

I want to make a relatively simple UI component for a character creation screen that looks something like this:

enter image description here

The structure is something like:

Canvas
-Panel
--Dropdown 1 Container
---Header
----Collapsible Body (Set as inactive)
-----Buttons for options
-----Buttons for options
-----Buttons for options
--Dropdown 2 Container
---Header
----Collapsible Body (Set as active)
-----Buttons for options
-----Buttons for options
-----Buttons for options
--Dropdown 3 Container
---Header
----Collapsible Body (Set as inactive)
-----Buttons for options
-----Buttons for options
-----Buttons for options
--Dropdown 4 Container
---Header
----Collapsible Body (Set as inactive)
-----Buttons for options
-----Buttons for options
-----Buttons for options
...

Unfortunately, some basic parts of this seem to be beyond my ability.

The panel that contains everything has a Vertical Layout group, but the "Spacing" can only be set to one value at a time, which doesn't work for this because the drop down boxes alternate between two sizes depending on whether they're active or not. Grid Layouts don't seem to work either, because the cell sizes can't be changed dynamically.

Is there a simple option for this, or would I need to code something myself?

Thanks for any info you can provide.

\$\endgroup\$
6
\$\begingroup\$

Alright my dude. Welcome to the hell that is layout groups. Here's a few things you'll need to know first:

  1. If nothing is sizing the object, the layout group will not work as you expect (in fact it appears the opposite). Things that size the object include LayoutElement and ContentSizeFitter. For whatever reason the default rect transform sizings make the layout groups work opposite how you expect.

  2. Layout groups CAN dynamically size, but sometimes when working with them via script, they don't update their layout fast enough. You may need to make calls to LayoutRebuilder.ForceRebuildLayoutImmediately()

  3. ContentSizeFitter will take it's children's prefered or min size (I usually use min) and make sure they fit those values. You'll want to make sure all your children of a ContentSizeFitter either have their own sizefitter OR LayoutElement. Set your LayoutElement min width/height to the width and height you want the element to be.

  4. All these layout classes like to "drive" different parts of the rect transform. Sometimes, when you change the settings or delete the layout, it will still say it's driving the values and you can't edit them. This is a bug. Duplicate the game object, and delete the old one and it will reset. This can also apply for when you change settings on LayoutGroups. If something doesn't seem right, it probably needs you to duplicate the entire layoutgroup gameobject, and delete the old one.

With that, let me explain how i'd handle this. Note, all content size fitters should be set to minimum in the vertical dimension. I Assume your Panel is the viewport for a unity scroll area so let's add a gameobject as a child of Panel called Content since Unity's scroll area reaquires a Viewport and Content. Content will have your vertical layout group and contain all the DropdownContainers. Each DropdownContainer should have a ContentSizeFitter that will resize it's rect transform to it's contents. Sometimes unity will yell at you for this, but just ignore it. It's BS. Inside, your Header needs a content size fitter as well. This let's it scale to the size of the collapsableBody. Now when this collapsable body resizes, you also need to know it's size. If you're just expanding the height of the viewport, then update a LayoutElement on the CollapsableBody to match the height of the viewport at all times. Just make sure you somehow declare to the ContentSizeFitter on Header that it's getting bigger. For the elements in the header visuals, they all need to have LayoutElement on them with their min width and height defined as well. Just remember, anything that doesn't have LayoutElement or ContentSizeFitter or something of the sort will be invisible to the ContentSizeFitter. Now that we have all the internal elements resizing correctly, add a ContentSizeFitter to the Content alongside the VerticalLayoutGroup. It will give you a warning. Ignore this. It's a false positive. Now the Content of your scroll area will be sizing ALL that child content. Holy crap. Now. Finally. Let's set the properties on your layout group. Set padding/spacing to what you want. Child alignment is Upper Center, Child Controls Size should be checked for height. Uncheck everything else. If you want you can force child expand on width. This will mean when you make your Content box wider, the elements inside will scale. But to do this correctly you'll need to make sure all the children are open to being scaled, and their rect transforms are anchored to scale width wise.

That's it. Quite a mouthful. Overall, I found not only were layout groups incredibly buggy and awful to work with, but also they are very nonperformant. And relying on them may require you to force rebuild the layout which is very heavy. Unity's UI works amazingly when you scale from an outside-in approach. Using rect transforms and anchors can get you a long way. Scaling from the inside-out, is only necessary with dynamically added elements. Like an inventory, or your dropdown UI. My advice is to design your UI to be as outside-in scalable as possible. What I mean by that, is have it so you can change the parent rect transform, and all the inner objects will grow/shrink to match instead of scaling the children and making the parents fit their contents. Consider tabs, pages, static lists over resizing elements. try to limit layoutgroups to inside scroll areas.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ "design your UI to be as outside-in scalable as possible" Thanks for this particularly. Auto-layout calculations, especially involving dynamic text, are surprisingly heavyweight. \$\endgroup\$ – MandisaW Oct 6 '19 at 16:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.