Unity GUI: resolution independence

When using Unity GUI, you usually want your UI to be resolution independent. And you never know what resolution the user will run your game with, when building for desktop (or floating canvas webplayer).

Unity GUI works on absolute screen pixel coordinates, where (0,0) is top left corner of the screen, and (Screen.width, Screen.height) is bottom right corner.

First thing I do when working with Unity GUI is picking neutral resolution. I design my UI to be ideal for this resolution. Let’s say my neutral resolution is 1024 x 768 and I have three GUI elements:

GUI.Box ( new Rect (0, 0, 1024, 768 ), "full screen box" ); 
GUI.Box ( new Rect ( 10, 10, 400, 400 ), "top left box" ); 
GUI.Box ( new Rect ( 1024 - 410, 768 - 410, 400, 400 ), "bottom right box");

Screen Shot 2013-11-04 at 10.30.36

It looks as I expected, as soon as my game runs in 1024 x 768, which was picked as neutral resolution.

When I  run this code in the editor, and won’t allow game window to maximize – my UI will not scale itself to fit the window. It will rather extend beyond the viewport, as you can see on the screen below.

Screen Shot 2013-11-04 at 10.32.00

There are two (well maybe more, but I will focus on two) ways to make this code resolution independent: docking and scaling. Each of them has its advantages, and disadvantages which depend mostly on the effect you try to achieve. Main differences between them is: do we fix positions, or do we fix sizes?

I will use game window as test environment for these two methods.

Docking

What would happen, if I changed hardcoded constants 1024 and 764 to Screen.width, and Screen.height accordingly? Let’s see:

float width = Screen.width; 
float height = Screen.height;
GUI.Box ( new Rect (0, 0, width, height ), "full screen box" ); 
GUI.Box ( new Rect ( 10, 10, 400, 400 ), "top left box" ); 
GUI.Box ( new Rect ( width - 410, height - 410, 400, 400 ), "bottom right box");

Screen Shot 2013-11-04 at 10.42.48

The positions of elements are not absolute anymore, but relative to screen edges and this relation is fixed. In other words – positions are always relative to screen edgesa and this relation does not change with resolution change. I call this method docking, because it “docks” x and y coordinates of each control with one of screen edges (x with left or right, y with top or bottom).

// docked to left and top edge of the screen 
GUI.Box ( new Rect ( 10, 10, 400, 400 ), "top left box" ); 
// docked to right and bottom edge of the screen 
GUI.Box ( new Rect ( width - 410, height - 410, 400, 400 ), "bottom right box");

Other thing we can see is, that the boxes now occupy most of the screen and top-left box overlap the text of the background box.

This drawback results from the fact, that box areas did not change, when we changed resolution. This is great advantage if you want your UI to be pixel perfect, but becomes problematic when you have big controls and need to switch to lower resolution (like in our scenario).

Scaling

When using scaling, boxes positions change when changing resolution, but relative area of boxes remain the same (they occupy the same percentage of area they did before resolution change). Example of scaling code:

float xFactor = Screen.width / 1024f; 
float yFactor = Screen.height / 768f; 
GUIUtility.ScaleAroundPivot( new Vector2 (xFactor, yFactor), Vector2.zero); 
GUI.Box ( new Rect (0, 0, 1024, 768 ), "full screen box" ); 
GUI.Box ( new Rect ( 10, 10, 400, 400 ), "top left box" ); 
GUI.Box ( new Rect ( 1024 - 410, 768 - 410, 400, 400 ), "bottom right box");

Function GUIUtility.ScaleAroundPivot changes GUI transformation matrix, so that each GUI control drawn below it will be scaled by the values of Vector2 we passed to it.

Screen Shot 2013-11-04 at 10.45.59

As you can see, I didn’t have to change UI drawing code at all – which is a big plus of scaling, when you have lots of controls which positions depend on each other. But also, there is a drawback. You can forget about pixel perfect when scaling the UI, as absolutely everything will be scaled up/down to maintain the relative size of neutral resolution.

Each of these methods can be expanded for actual needs (ie. docking with size modifications, scaling with UI centered etc.), but this is out of the scope of this post. To sup up – I use docking whenever I need my UI to be pixel perfect. But whenever I do not, or I want to have scaleable UI in the editor – I pick scaling.

One comment

  1. This is a GREAT tutorial. I was researching this topic for a while and your blog post helped point me in the right direction.

    I’d also like to include for other people reading this blog that you can format GUIText size by changing the “fontSize” relative to the xFactor.

    Example: GUITextObject.fontSize = Mathf.FloorToInt(originalFontSize * Screen.width/960);

Leave a Reply