GUI best pratices?

Every time I design a GUI in Unity (especially something like a menu or options screen), I feel like I'm doing something wrong by including all the textures and positions and everything in variables in the script. When the GUI becomes increasingly complicated this can get really messy, really fast.

So what's the best way to go about creating a GUI? Where should you place all your texture references? What about your strings? What's the best way to center something? What's the best way to combine a GUISkin/GUIStyles with code? I just feel like every time I code a semi-complicated GUI I'm doing something wrong.

I realize this is a lot of tiny questions in one, and I'm just sort of looking for some pointers/tips on the topics I mentioned. Thanks.

Edit: I really don't want to start a bounty on this question... noone has ANY ideas?

Semi-personal note: I had a project on hold, as I was going to wait for the answers from this question so I could do the GUI in the best practical way possible, however I can't hold it off any longer. I guess I'll just do it the old-fashioned, dirty way until someone comes up with a solid answer.

I'm hesitant to speak on this too much since I have yet to do heavy GUI work in Unity, but from a theory standpoint there are two main mentalities I've seen at work-

1) Decide your resolution and keep to it. Having spent a few years at the fringes, and more recently diving into the Industry proper, I see a lot of artist/designers who want to know that the interface will be ~exactly~ as they design it. So they do up the interface in precisely-sized native-resolution art to fill the screen just-so and it all gets either hardcoded in as you first describe, or I think (though I have little reference) more frequently parameterized out to a config file. Each resolution the game can run at gets logged as a set of config params to select which assets to place where, and/or which asset set to use, and other resolutions get disallowed. I'd be surprised if there isn't some structure that you can socket into a script var in Unity that could hold that kind of info.

2) Everything is a percentage. Define the position and scale of assets once, normalized to the window size. This works well for vector graphics (and by extension most Flash content), but less well for bitmap assets. If the user changes the window size at runtime, the exact same layout script used at launch can put everything in its proper place after a change. As long as your interface assets themselves are scalable, and you can get their size-as-loaded, this will even let you switch out skins during development without changing too much under the hood. However, bitmaps rarely scale well, so you may need to just define positions and force a minimum window size limit.

Either way, you may get milage from defining some wrapper classes. E.g., if your interface has a health bar (with numeric readout), a magic bar (with numeric readout) and a score meter, and those are always in roughly the same place well and apart from a game timer, well and apart from a hotkey bar, rather than having each item tracked loose in one huge GUI script, try making a VitalsBox object that has your bars, gold & associated text, a MissionTimer object with game time and an optional field for e.g. a time limit or quest detail, and a HotKeysBar object with any and all finnickly little icons you may want to access. Then put an instance of each of those three in your main GUI script. It might even help to create a base class first, with tracking for total-bounding-box-size and other useful generic parameters, and spin your specific interface containers off of that. That way, your main GUI code stays small, and if you play it right, you can make new versions of each component object and socket ~them~ into vars in your main GUI as needed. Further, whenever an asset changes, if you've done your job right the number of other elements that need to move to accommodate it is limited to the scope of the container object it's part of, rather than the whole interface.

This post may go unnoticed because it's 3 months late, but my GUI design theory is as follows:

Disclaimer: I'm new to unity and its GUI, so this is just my method du jour.

My Main GUI is fairly intense, there are numerous areas with scrolling data, others are just buttons with images (via skins), some are drop down menus with sub menus and of course, a large scrolling text area. With that said, it's obvious that I have a lot of separate GUI Elements on screen at one time and all of that GUI Coding can get REALLY messy. My approach is to create a single OnGUI function within a script called "GameManager.js". Along side of this script, I create a separate JavaScript file for each GUI Element I use throughout and then reference them in my GameManager file. It keeps things simple, compact (code wise) and I can have other developers modify the "ListOfStuff GUI Element" while I work on the main control for the other elements .. or the main layout script.

If a GUI element isn't "worthy" of its own script, I'll create a separate function for that piece and then reference that function within the OnGUI for display.

Here's an example that includes both techniques: (hope this helps!)

------------------- GameManager.js ----

var mainGUI;    
var defaultSkin:GUISkin;    

function Start()    
{    
    mainGUI = this.GetComponent("MainGUI");    
}

function OnGUI ()     
{       
    GUI.skin = defaultSkin;    
    mainGUI.display();    
}

-------------------- MainGUI.js ----

private var GMgr:GameManager;

function Start()
{
    GMgr = this.GetComponent("GameManager");
}    

function display()
{
    createMainGUI();
}    

function createMainGUI()
{
    // Top GUI
    displayTopGUI();    

    // Bottom GUI
    displayBottomGUI();
}    

function displayTopGUI()
{
    GUILayout.BeginArea (Rect(0,0,Screen.width,200));
        GUILayout.BeginHorizontal ();
            topMenu_File_Button();
            topMenu_Window_Button();
            topMenu_Header();
        GUILayout.EndHorizontal();
    GUILayout.EndArea();
}

This might not be the optimal answer, but... I guess it really just depends on your project, and how "messy" you want things to be. GUI Helper classes are always a plus, for instance, I just wrote this for my game, because I had found I would always want to anchor some GUI element to a spot on the screen easily:

using UnityEngine;
using System.Collections;

public enum AnchorType
{
    TopLeft,
    TopCenter,
    TopRight,
    MiddleLeft,
    MiddleCenter,
    MiddleRight,
    BottomLeft,
    BottomCenter,
    BottomRight
}

public static class GUIAnchor
{
    public static Rect PositionFixed(Vector2 size, Vector2 offset, AnchorType type)
    {
        return PositionFixed(size.x, size.y, offset.x, offset.y, type);
    }

    public static Rect PositionFixed(float x, float y, Vector2 offset, AnchorType type)
    {
        return PositionFixed(x, y, offset.x, offset.y, type);
    }

    public static Rect PositionFixed(Vector2 size, float xOffset, float yOffset, AnchorType type)
    {
        return PositionFixed(size.x, size.y, xOffset, yOffset, type);
    }

    public static Rect PositionFixed(float x, float y, float xOffset, float yOffset, AnchorType type)
    {
        float top = 0;
        float left = 0;

        switch (type)
        {
            case AnchorType.BottomCenter:
            case AnchorType.MiddleCenter:
            case AnchorType.TopCenter:
                left = Screen.width / 2 - x / 2;
                break;
            case AnchorType.BottomRight:
            case AnchorType.MiddleRight:
            case AnchorType.TopRight:
                left = Screen.width - x - xOffset;
                break;
            case AnchorType.BottomLeft:
            case AnchorType.MiddleLeft:
            case AnchorType.TopLeft:
                left = xOffset;
                break;
        }

        switch (type)
        {
            case AnchorType.TopRight:
            case AnchorType.TopLeft:
            case AnchorType.TopCenter:
                top = yOffset;
                break;
            case AnchorType.MiddleRight:
            case AnchorType.MiddleLeft:
            case AnchorType.MiddleCenter:
                top = Screen.height / 2 - y / 2;
                break;
            case AnchorType.BottomRight:
            case AnchorType.BottomLeft:
            case AnchorType.BottomCenter:
                top = Screen.height - y - yOffset;
                break;
        }

        return new Rect(left, top, x, y);
    }

    public static Rect PositionScaled(Vector2 size, Vector2 offset, AnchorType type)
    {
        return PositionScaled(size.x, size.y, offset.x, offset.y, type);
    }

    public static Rect PositionScaled(float x, float y, Vector2 offset, AnchorType type)
    {
        return PositionScaled(x, y, offset.x, offset.y, type);
    }

    public static Rect PositionScaled(Vector2 size, float xOffset, float yOffset, AnchorType type)
    {
        return PositionScaled(size.x, size.y, xOffset, yOffset, type);
    }

    public static Rect PositionScaled(float x, float y, float xOffset, float yOffset, AnchorType type)
    {
        if (x < 1)
        {
            x /= 100;
        }
        if (y < 1)
        {
            y /= 100;
        }
        x = Screen.width * x;
        y = Screen.height * y;

        return PositionFixed(x, y, xOffset, yOffset, type);
    }
}

It's not great, it doesn't do much, but it's an example of some GUI code that can be wrapped up statically in another class, making your other code less sloppy.

Anyone else have any other examples like this? And perhaps someone could give me some feedback on this class, perhaps things that could be added to it, or potential bugs.

Some useful things you can do include:

  • Decide on your "canonical" screen resolution and scale things accordingly based on your actual runtime resolution -- designers can create art for your primary resolution (say 1024x768, if you're an iPad fan, or maybe you prefer 1920x1080 for full HD support), but you can resize all your art on the fly to fit 480x320 in your iPhone build.
  • Know your audience and target platform -- review Unity's web player statistics and the Steam hardware survey and decide how your application will most commonly be viewed.
  • Yes, textures get slightly fuzzy when they are resized, and yes, you will use more disk space/download time/memory than necessary -- when the time comes to publish a build, you can resize artwork in the pipeline, but design-time flexibility is very handy for speeding up iteration.
  • Althought Unity does handle resolution independence well, you still need to consider change in aspect ratio -- are you going to show more on one side? crop the other side? or squash things? (4:3 is common, or 16:9 for HD).
  • Use skins to consolidate constants, colours, and texture references -- GUISkin is a good example, and you can get plenty of mileage from GUISkin itself, but you can also use the same concept for your own "skin" classes -- create a class that's nothing but public fields, create (typically) one instance of it in your scene, and reference it from any OnGUI method that needs to know those icons/colours/fonts/offsets/scale parameters/etc.
  • Save these skins as assets, rather than components in the scene -- you can put a GUISkin in your Project view directly, or a prefab that references your custom skin. The benefit of this is that a skin tweak does not require a scene change, a definite boon to your version control issues if you are using Unity Free on a multi-member team.
  • Learn how to use the capabilities of the GUI API to full advantage -- for example, use GUI.color to tint a font or grayscale texture, so you don't have to create multiple fonts/ textures; use GUI.matrix for sub-pixel positioning when you have moving elements; use GUI.matrix also for font resizing (but don't stretch a font up by more than about 1.5x).

Hope that's helpful to someone, and thanks to everyone for sharing their tips.

For me this is a question of where is the info that is displayed in the GUI coming from or related to? So in some ways it is analogous to arranging your assets folder. You could put all your textures in a textures folder, all your models in a models folder etc, but is that a sensible arrangement? As the project gets larger you end up with folders full of stuff only related by the fact they are textures models etc.

So back to the GUI. I have a guiscript that manages drawing the GUI and contains references to the standard assets used for the static or non specific elements of the GUI. However a reasonable Chunk of GUI code and asset references are in scripts attached to the gameobjects they relate to.

Eg. A leaderboard for a race. It has a standard frame but each player in the race has a different logo and distinctive colour, plus it has player specific values that get printed in the leaderboard entry for that player.

So, the guiscript has a reference to the leaderboard frame and the code to iterate through the players in the race and get their leaderboard entries let's say by calling the getLeaderBoardEntry(x:int,y:int) function on each player. Each player will have a reference to their color, logo image and of course it's data. That function will take in the screen origin point of the leader board entry and draw textures text and colour the entry appropriately returning let's say the width And height of the entry as a vector2 so that the guiscript can pass the correct coords to the next player.

That's a fairly specific example about how I go about things in general but this approach has quite a few advantages in general I think.

It becomes easier to reuse your guiscript code because it is more general since the specifics are called elsewhere. It is easier to search for things to debug because they are somewhere relevent. It is easier to customise. Ie. In the above I could have all sorts of players with different leaderboard entry code meaning each leaderboard entry could be quite different depending on what the player was.

Hope this is sort of helpful in some way.

start a windows GUI program in cygwin