Fully Responsive User Interface with Unity GUI

August 12th, 2013

Unity GUI gets a lot of criticism, and there are reasons for that: it’s not visual at all, you have to code everything, most of the time you can’t use it on mobile due to high redraw frequency and excessive drawcall use, etc. I agree with all that. However, it has a number of important advantages as well: unlike most of the external libraries and plugins from the Asset Store, it is integrated into the engine really well, and it is completely free. You can’t say that about EZ GUI or NGUI, now can you? This last reason is probably the main thing that makes people use Unity GUI, and they are right to do so. Why pay $100+ when you can get the same result for free?

I have used Unity GUI on a variety of different projects and in a variety of situations. Over the years I have dug up a bunch of really useful tricks that help me code a basic interface in a matter of minutes. It was a couple of weeks ago that I decided to gather this stuff in one class that helps me overcome the main bottleneck of Unity GUI: its inherent lack of responsiveness. The term “responsive” comes from web design, and means that the interface adapts to the screen resolution. A modest example of responsive web design is my web site. Try resizing the window of your browser and see how everything scales and moves around. When you are making a cross-platform or PC game, you want it to be able to adapt to resolution as well, and to do that, the interface elements have to stick to their part of the screen and retain their size relative to the screen size.

My class is based on the basic idea behind Automagic GUI Scaling in Unity3D by Simon Wittber: you choose one resolution that you consider the “base” resolution, defined as WIDTH and HEIGHT constants in the beginning of the script (usually it will be the most popular resolution of your intended audience), then resize the GUI elements using GUI.matrix. The difference is that in Simon’s example you will end up with “offsets” on top and bottom of the screen if your screen ratio is different from the resolution that you specified with WIDTH and HEIGHT, which we don’t want. I solve this problem by applying the resize method to different regions of the screen separately, thus effectively masking the annoying offsets.

Now that we’re done with theory and explanations, you can get the GUISizer class here: http://goo.gl/wgdFX6. You can also download an example project here: http://goo.gl/5F5PIY. Or, if you prefer, the same thing as a UnityPackage that you can import into your own project: http://goo.gl/GKczDm.  The source code is also added to the bottom of this post in case the links die. If you use C#, then feel free to put GUISizer.cs anywhere you want in your project. However, if UnityScript is your cup of tea, then it has to go into your Plugins folder. Otherwise you won’t be able to access its methods and structures.

With that out of the way, you can start poking around in the test project. To test how it works, just try resizing your Game view window. Below is some API with explanations, as well as the source code. Feel free to ask questions or suggest adding or changing things in the comments or elsewhere. You can use GUISizer in any commercial or noncommercial projects as you see fit. And of course enjoy the fully resizable Unity GUI!

P.S. As a bonus track, in the example project you will find a script that lets you position a GUI element in world coordinates (like the text label that you can see sticking to the cube in the video above).

Struct: 

struct GUIParams

This struct lets you create a variable of type GUIParams: a button or a label, with specific parameters that you pass to the constructor.

Constructors:


Methods:

void BeginGUI(PositionDef elementsPosition = PositionDef.middle)

This method has to be called in the beginning of a GUI block, the parameter that it takes is the position of the elements in the block on the screen.

void EndGUI()

This method has to be called in the end of a GUI block. There has to be one EndGUI for each BeginGUI. What’s between the two is a GUI block: buttons and labels.

Rect MakeRect (GUIParams guiParams)

Converts GUIParams into a Rect.

bool ButtonPressed(GUIParams guiParams)

bool ButtonPressed(GUIParams guiParams, int customFontSize)

bool ButtonPressed(GUIParams guiParams, GUIStyle style)

bool ButtonPressed(GUIParams guiParams, int customFontSize, GUIStyle style)

Checks if a specific button was pressed. Takes a GUIParams struct as a parameter. You can also provide a custom style and a custom font size as a parameter.

void MakeLabel(GUIParams guiParams, string additionalText = “”)

void MakeLabel(GUIParams guiParams, int customFontSize, string additionalText = “”)

void MakeLabel(GUIParams guiParams, GUIStyle style , string additionalText = “”)

void MakeLabel(GUIParams guiParams, int customFontSize, GUIStyle style , string additionalText = “”)

Draws a specific label. Takes a GUIParams struct as a parameter. You can also provide a custom style and a custom font size as a parameter. Additionally, you can provide text that will be appended to the label, which is useful for things like score and lives.

Variables:

float WIDTH = 960;

The “base” width of the screen. This is the width of the screen relative to which all GUI elements will be resized. Change it directly in GUISizer script.

float HEIGHT = 600;

The “base” height of the screen. This is the height of the screen relative to which all GUI elements will be resized. Change it directly in GUISizer script.

const float SMALL_BUTTON_WIDTH = 100f;

const float SMALL_BUTTON_HEIGHT = 50f;

const float MEDIUM_BUTTON_WIDTH = 200f;

const float MEDIUM_BUTTON_HEIGHT = 100f;

const float LARGE_BUTTON_WIDTH = 300f;

const float LARGE_BUTTON_HEIGHT = 100f;

const float BUTTON_GAP = 10f;

enum PositionDef { topLeft, topMiddle, topRight, bottomLeft, bottomMiddle, bottomRight, left, right, middle }

Enum that is used to define and identify “anchor” positions on screen. Positions themselves are set in the private method SetPosition.

enum SizeDef  { small, medium, big }

Enum that is used to define and identify sizes of elements. Sizes themselves are set in the private method SetSize.


Source code of the GUISizer class, as promised: http://pastebin.com/MDazhK6L


						
			

Coding, Unity3D | Comments | Trackback

  • Yaser

    I totally agree with your post. Even on Mobile you can get a decently performing GUI if you are applying some calculations and avoid using GUILayout class (disabling it)

    However, recent requirement to draw 3D objects on top of GUI forced me to use a GUI frameworks like NGUI since you cannot do that with OnGUI

    • http://sergeymohov.com/ Sergey Mohov

      Yeah, I didn’t use GUILayout in my GUISizer class on purpose, as I’m aware of the drawbacks.

      And yes, drawing 3D objects on top of GUI is impossible indeed, which, if you ask me, is a flaw in Unity’s rendering pipeline. A camera drawn after another camera should always be on top, GUI or no GUI. Oh well.

Categories

Find:

Links