Megabite #11 - The Basics and Capabilities of the Unity GUI System

One of the most important things to make note of with the Unity GUI system is that everything within the OnGUI () function will run twice per frame (Correction thanks to AngryAnt via the comments: OnGUI is not run twice per frame. It’s run once per event. By default that is two – one layout event and one repaint event. If, however, you have mouse or key events you’ll get more invocations) .  While you can easily use variables to manipulate the size and position of windows, boxes, buttons, etc., you'll want to do the calculations for those variables in other places and avoid using loops or complicated formulae within the function.

When it comes to displaying a basic GUI you have a few easy options - GUILayout or manual placement.  Depending on the look you want, you may be able to use either one but with different complexities.

As an example, let's start off with GUILayout, which attempts to automatically lay out the components of your GUI.

function OnGUI() {
GUILayout.Button("Here is a test button");
}

In this simple piece of code, the result is a labeled button on your game screen.  Because we didn't declare anything about where we wanted to place it or declare a size, it appears in the upper left of the screen and is as long as the text it contains.  Making it do something is as easy as making it into an "if" statement:

function OnGUI() {
if (GUILayout.Button("Here is a test button")) {
DoSomething();
}
}

In this case, pressing the button will run the function "DoSomething()".  To use the manual option for this same thing, let's set up a test button:

function OnGUI() {
GUI.Button(Rect(0,0,200,50), "This is a test button");
}

What we've done here is manually place the button at screen position x=0 and y=0, which is the upper left just like the other button.  However, we've manually set up the width as 200px and set a height of 50px.  Whether you use boxes, buttons, labels, or any other GUI elements, you can manually set them up using:

GUI.[element] (Rect( X-Position, Y-Position, Width, Height ), "(Text here if applicable)")

Typically I prefer to have this level of control over each element, and it allows for more variables to be easily inserted.  However it does make your code a bit more complicated.  As a result, the majority of the examples here will be focused on using GUILayout.

When placing GUI elements, I often times want to make things resolution independent.  Because you don't want someone with a 30 inch monitor forced to play your game at 800x600 unless they choose, you can use the default Screen variables to get numbers based on the resolution of the game.  For example:

function OnGUI() {
GUI.Box(Rect ( (Screen.width / 2) - 100, ( Screen.height / 2) - 100, 200, 200), "This is a centered box." );
}

Manually Created

Now, getting back to doing the same with GUILayout - as you can imagine we do need to tell Unity where to actually draw our box.  The process is very similar to what we've just done:

function OnGUI () {
GUILayout.BeginArea (Rect ( (Screen.width /2) - 100, (Screen.height /2) - 100, 200, 200));
GUILayout.Box("This is a centered box.", GUILayout.ExpandHeight(true));
GUILayout.EndArea ();
}

By comparison, this may look more complicated than than manual placement, but it allows for easy placement of more elements.  It will always be at your discretion which method you prefer for what scenario.  My best suggestion is to familiarize yourself with both options and all of the available elements in Unity GUI.

Next, we're going to work with using the GUI for functionality.  This includes text input, sliders, and selecction grids (which work similar to "radio buttons" in HTML).

Text Input

For text, what we have within an input field is a variable.  So, let's start with setting up our String variable:

var myVariable : String = "Default Text";

We want to do this outside of the OnGUI function.  When we set up the GUI, all we have to do is be sure that myVariable is directly equal to whatever is inside that text box.  Also, we set up the max length of the text box.

Input

function OnGUI() {
myVariable = GUILayout.TextField (myVariable, 30);
}

When the game is run here, you will be able to directly edit the text box.  myVariable can be called from any other function in Unity and the result will always reflect what is currently in the box.  Most commonly, a button will perform the functionality you need, like this:

var myVariable : String = "Enter Name";

w/ button :)

function OnGUI () {
myVariable = GUILayout.TextField (myVariable, 30);
if ( GUILayout.Button("Accept") ) {
DoSomething(myVariable);
}
}

function DoSomething (name : String) {
print (name);
}

Utilizing text fields and text areas is nearly the same.  Depending on your needs, you can use TextField for single lines, TextArea for multi-lines, and PasswordField for password-like entries.

Sliders

Using sliders is just as simple as text-type elements.  Set a variable, figure out a minimum and maximum value, and pop it in.  When using GUILayout, it's important to note that the size of the slider is typically related to other elements.  If your code has no errors but you can't see your slider, this is usually the case.  Example:

var mySlider : float = 50;

function OnGUI() {
mySlider = GUILayout.HorizontalSlider ( mySlider, 0, 100);
GUILayout.Label ("The slider will be as long as this GUI Label.");
GUILayout.Label ("Current Value: " + mySlider);
}

In this example we've set the minimum of the slider to 0, and the max to 100.  If you need a vertical slider, the code should be exactly the same.  As with the above example, if you want to use the current value in some other function, it's as easy as adding a button or otherwise accessing the mySlider variable.

Selection Grid

A selection grid is a series of buttons that allow a user to choose one option from a list.  Of course, this is almost the same functionality as a radio button on a web site and is typically understood quite easily.  To use a selection grid, you have a single variable that sets up your choices, and clicking one down in-game will mark it as selected.  In effect, this will simply change your original variable value (to a number) for usage however you like.  Example:

var returnValue : int = 0;
var myChoices : String[] = ["Choice 1", "Choice 2", "Choice3", "Choice 4"];

function OnGUI () {
returnValue = GUILayout.SelectionGrid (returnValue, myChoices, 4);
if (GUILayout.Button ("Print Value")) {
print (returnValue);
}
}

In the above example, I used "4" as the X-value.  This number influences how the grid is layed out by saying how many columns you would like to set up.  So, I chose a horizontal line of buttons where "1" yould have been a vertical line or "2" would have been more of a box-shape.

A selection grid can be a very nice addition to a gui for a multitude of reasons, and the simplicity of working with it can be a godsend.

Button vs. RepeatButton

Most of the time, GUI buttons are made to be clicked on once, and something happens.  This may not be the case if you are using something like a mobile device though, which may requre that for as long as something is held down, something happens.  For that scenario, whe you'll need is a RepeatButton.  In terms of true/false, as long as the button is held down, it registers as "true".

For a working example, we'll show the difference between the two here:

function OnGUI() {
if (GUILayout.Button("Regular Button")) {
print ("Registered regular button input");
}
if (GUILayout.RepeatButton("Repeat Button")) {
print ("Repeat button input");
}
}

When you click the two buttons, the difference will be clear in the console.  Since OnGUI runs twice per frame, the result from the repeat button should stream in quite quickly.  In application, something like this might be perfect for on-screen mobile controls, buttons that add or subtract to a number based on time clicked, or other nifty features.
If you get stuck when attempting your own GUI integrations, you can feel free to ask away here or shoot me a tweet and I'll get back to you asap.  If theres enough call for it, I may devote the next article towards properly skinning GUI elements and working with the basics of styles and coloration.

Fairly soon I will have a big announcement concerning a project I've been working on, and I know my readers here should enjoy it.  Until next week, happy programming!