Posted by Abban on 05/02/2017
Leveraging Unity Components to achieve Flexible GUI Layouts
While I've been fiddling with Unity and building prototypes for a few years now, I only really started building my first game in early 2016. At that time I hadn't delved too deep into GUI and found it completely bewildering, I had no idea how make my GUI work across different device sizes. I knew responsive design techniques from web development and how Xcode Interface Builder works but searching that stuff in the context of Unity didn't point me in any good direction.
Then while working on BIG Bad Spanish I accidentally discovered the Unity layout components. These components are exactly what I needed, are easy to use, and it made me wonder how I missed them before. Like a lot of Unity functionality they're buried pretty deep in the documentation and if, like me, you don't like video tutorials its very hard to get an overview of whats available. Google also doesn't index the content of YouTube videos.
So in this post I'm going to go over the layout components with some examples of the patterns I use the most. The goal is to expose this functionality to people like me who rely on text searches over video tutorials.
Here's a list of the components I find the most useful.
The Canvas Scaler is a component that when added to a canvas element allows you to control how that canvas reacts to different screen sizes.
On the development of our mobile game I've been setting this to scale with screen size and setting the target device to a large iPhone resolution. This means all our GUI graphics are made to the maximum size needed and can be scaled down for other devices later.
The drawback of this is Unity doesn't auto rescale the GUI sprites to non-retina sizes to be used on older devices. They released a stop gap solution but I think they're really trying to wait until this isn't an issue anymore. If you want to target an older device specifically you'll need to manually scale the sprites.
Also, setting the scaler to Constant Pixel Size probably won't work on PC/Mac/linux builds as your GUI will need to be a constant screen size and there are way too many different resolutions to handle. You'll end up with giant or tiny GUI elements on some resolutions.
This isn't really a layout component but I find it really handy so I'm including it here. This component is a control for setting the alpha, interactability, and raycast blocking of child elements. It's very handy for fading elements such as modal windows in and out.
These are the meat of the layout components. They will automatically control each of the height, width, margins and spacing of child objects if you so desire.
They these are probably the 2 most important components when it comes to making a GUI. You can nest them and nest them and nest them to create complicated layouts. You can also set all their children's margins and spacing from one place.
Some bad news is that they can be a little inconsistent. You can't define a percentage width for the child of a layout group meaning Rows or columns with nested groups can force themselves to be bigger than their sibling rows or columns. A solution to this is to nest each child layout group in an empty game object container. This will stop the child group affecting the parent group's size.
I'm not a big fan of the Grid Layout Group. It takes too much control of the size of its children and doesn't allow percentage widths or heights. I find nested Horizontal and Vertical groups to be a lot more flexible. But I'm including it as you should be aware it exists.
Layout Elements are used when you want to control some aspects of the children of Layout Groups. You can tell a child to ignore the parent leaving it free to manually position. You can give it a minimum or preferred width and height. Also, you can set them as flexible in order to make use it fills up available space.
Again, there's no percentage widths. I feel that these elements would be a lot better if I could just tell them to expand in a direction and set a fixed percentage in the other. That would allow full percentage layouts similar to CSS.
Here's a few more complicated layouts you can achieve with the Layout Groups.
So to make a modal is fairly easy to do using a canvas group. I like to add the component to the overlay image and have the modal content a child of that. Then you just need to use a Coroutine to fade it in and out. You can see an example of the Coroutine in the example repository.
The trick to this is to turn off Force Expand in the direction you want your child to expand into in the Layout Group. You then add layout elements to each of the children, set a min height or width on the ones you don't want to expand and set flexible height or width on the one you do want to expand. Here's an example of a Vertical Layout Group:
- Vertical Layout Group (Force Expand Height: off) - Child Layout Element 1 (Min Height: 100) - Child Layout Element 2 (Flexible Height: true) - Child Layout Element 3 (Min Height: 100)
This will make the middle element expand to fill the space not used by the first and last. Again there's an example in the repository.
Again this is easy enough with Layout Groups. You can achieve it by first creating a Scroll View, then adding a Content Size Fitter and a Layout Group to the Content object in the Scroll View's Viewport. When you instantiate an element directly into the Content object it will call a refresh on it and automatically adjust the length of the scrollbar and Content element. And of course, theres an example in the green area of the sample project.
Thats the basics of how we do all our layouts, we also have a few more detailed components that we use such as tabbed views or pages sliders. They're too detailed to write about in this post so I'll cover them in the future. If you have any feedback please feel free to shout at me on Twitter.