Recently I was asked to explain how to build an adaptive UI anchoring and hierarchy for a mobile game. So I decided to write a post regarding this topic.
During my experience with Unity and playtesting different games, I mentioned, that lots of developers don’t pay proper attention to scalable UI for different aspect ratio, either on mobile or tablet device.
To start with, you need to decide on which orientation you would like to build your game, usually, you’ll choose only one - portrait or landscape. In some cases, you can target both and control which one to enable in code, but it’s better to target only one.
In this showcase project, I will build an adaptive quest panel and explain each part of realisation, as usual, at the end of this post, you may find git project, build following the same steps where I created shop UI system.
As shown above, I usually set UI scale mode in canvas scaler component to scale with screen size, as it helps us to resize our canvas and its components respectively to our reference resolution.
When I start with a new canvas, I firstly choose target aspect ratio, from which canvas change objects scale respectively to proportions. I usually use 1920x1080 resolution. As we build landscape UI, in screen match mode we need to match Height, or simply enter the “1” value, instead of “0.5”. It simply helps to use the same components ration on different devices.
After setting your canvas components, my advice is to add as a child, new UI panel component and scale it to the full size of the current canvas. We will use this panel as our main anchoring object to build other components above it and scale them respectively to this parent.
We will use a lot of panel components for better anchoring in this example. You can use empty game objects instead, but I mainly prefer panels, as you have an image component on it. This image helps me to recognise current anchoring and objects borders better. Afterwards, you may use these image objects, as a holder for UI sprites if needed or simply delete them, when you finished with UI settings.
On the screen above is the final result. As you see, I divided this panel into 3 parts:
- Top — where is quest image, quest name and horizontal scroll with rewards for the current quest.
- Middle — is basically for quest description, which scales horizontally and has scroll rect component attached.
- Lower panel — where I placed quest buttons to apply or decline the current quest.
So let’s split our “Quest_Giver_Panel” into 3 panels. They all are stretched horizontally and anchored on top, middle and bottom position respectively.
Let’s start with the easiest part — the bottom one. Here you only need to add 2 buttons apply and decline, As you see, they both are anchored to the left and right side respectively, stretched vertically and have only one difference, x position offset, decline button has 300 horizontal offset and apply has -300. At this point bottom panel is finished, let’s move to the next one.
We have a couple of components here, as I am expecting to have long descriptions for quests, I decided to add scroll view component and scale vertical text size accordingly to description size, so when text vertical size exceeds panel size, player can scroll down and get all text displayed.
Here we need to set up scroll rect settings for our needs, as we don’t expect to scroll horizontally, we need to uncheck this allowance as well as delete the scroll bar horizontal game object from the hierarchy.
After we need to add a couple of components to scroll view content, its vertical layout group and content size fitter. If you never used them before, my advice is to add them to your prior UI components and use them in your projects. Layout group helps us to align objects in a certain area, we can control objects size, scale and transform to apply better screen positioning and adaptive resizing according to the current screen resolution. In this project we will use a couple of layout groups, vertical and grid, there is one more, called horizontal, it’s mainly the same as vertical, but used to align objects horizontally.
Content size fitter helps us to control the size of child objects in a layout group, as our description text will expand regarding to the number of words, we need to control the vertical size of our content and avoid the situation, when on some resolutions we will have text displayed over other components below, so set Vertical Fit to a preferred size.
Here we need to place 3 different components, so to achieve appropriate view positioning we need to pay more attention to anchoring each component carefully.
Let’s start with Quest Image, we create a new panel only for the image, align it on the left side of its parent and stretch it vertically, it helps us to keep the same image ratio on different resolutions. After we can add to the image panel object a new image component and set its position at the parents centre. So whenever we stretch the whole UI panel, our Image will keep the same proportions and alignment.
Afterwards, we add a new panel to the top panel object and name it “Quest Reward Position”.
We need to anchor it to the middle and stretch horizontally. I’ve set the horizontal offset to 420 from the left, so it can be perfectly adjusted on different screens.
Now, let’s add scroll rect, here we will use it for horizontal scroll to show all available rewards, so in case we would like to apply a variety of them, we can scroll to see all. In this situation, I decided, that its better to use a grid layout group, because here you can control the size of each cell and spacing, just be sure to set a fixed row count to 1, so all elements will be positioned horizontally in one row.
Last but not least, you can add to this panel text component with quest name, just anchor it to the middle side, stretch it vertically and adjust the horizontal size of the text component.
Now you should have an adaptive UI panel, which will keep the same aspect on different devices.
Just create a couple of prefabs with reward icons and spawn them inside of the content object in the scroll rect component.
Some useful tips
Now I want to sum up the main ideas and mention necessary UI components:
1. Be sure to focus only on one resolution inside canvas settings, it will help you to build the same ratio on each UI canvas.
2. Don’t forget to set the match size according to your game orientation. 0 is always for portrait, 1 is for landscape. Try to avoid 0.5 value, as it could ruin your components adjustment.
3. When setting RectTransform values manually, use only numbers divided by 4, or at least by 2, it will help you to divide your canvas area properly.
4. If you are not planning to use any buttons or input components, delete all unnecessary components as Graphic Raycaster or deselect the raycast target option in the Image object. It will help you to improve your UI in terms of optimisation.
5. For optimisation purposes, I will advise you to divide UI into different canvases, for example, if UI will not change its values dynamically, try to separate them to another canvas. Because when you dynamically change text or image component during play mode, it will redraw not only changed components but static ones too. This causes more draw calls, which is bad for performance.
6. Don’t forget to use such UI components as Layout groups, Layout element and Content size fitter, they are always useful when you aim to get the same result on different resolutions. For example, a content size fitter would also be useful, when you create a shop system, here you can add a horizontal layout group on the buy button, and control price text size with size fitter, so whenever price text is longer than expected, it will clamp its size and avoid text overlapping.
I tried to cover all the necessary information regarding this topic. As always, I leave a link to Git-repo. There you will find another project of UI shop system because it’s always better to compare gathered knowledge in a different situation.
Thanks for your attention and time. Follow me on Twitter to get notified with my future posts.
Twitter — https://twitter.com/044Developer