GUI Tutorial

The purpose of this tutorial is to get you started with creating and working with GUIs in the engine. If you haven't done so yet, go ahead and read the first tutorial. This tutorial will assume you know the absolute basics like creating a scene and scene nodes.

We'll be creating a main menu which will have "Start Game", "Open Editor" and "Exit to Desktop" buttons. The "Start Game" button will simply load an empty scene - it's intended only to show you how to load and start a scene from another scene. We are not concerning ourselves with making the GUI look pretty.

0. How it Works

There is one particular detail about how the engine handles GUIs that you must be aware of - the engine has a single, shared GUI environment for the entire game, including the editor. What this means, is that whenever a GUI element is created, it will linger around until it's deleted. This is especially important when playing scenes from inside the editor.

Another detail is that GUIs are just part of a scene like anything else. You will probably have "GUI" scene nodes in the scene. For example, your HUD will probably be represented with a scene node.

GUI's are created via the scripting environment. You attach a script to a scene node and create and delete the GUI elements via that script. You will usually create the GUI elements in OnStartup() and delete them in OnShutdown(). GUI elements must be deleted!

GUIs work in a similar way to websites. When you create the element, you attach any number of CSS-like style classes for positioning and styling. The style classes are written in a CSS-like language and can be loaded from files.

1. Get the Sandbox

As usual, if you haven't yet done so, go grab yourself (hehe) a copy of the sandbox. If you've already got a copy of the sandbox lying around somewhere (such as from the previous tutorial), go ahead and use that instead if you like.

2. Setup the Scene

First, go ahead and create a new empty scene in any location you like. Then, create an empty node. You'll notice that the new node has no visual representation. To make things a bit easier, you can assign a sprite to the node by expanding the Editor Metadata panel, checking the Show Sprite check box, and then drag-and-dropping a texture (not a material!) into the text box. Use "engine/editor/images/gui-sprite.png" for a sprite that shows the words "GUI" on it.

GUIs are handled via the scripting environment. So go ahead and attach a Script component to your GUI node. While you're at it, you might as well create an empty script file and drag-and-drop it into the Script component. Call the script file whatever you want, but we'll call it "main-menu.lua".

3. Write the Script

The best way for you to learn how to create a GUI is with an example. Put this script into your "main-menu.lua" script file:

function self:OnStartup()
    GTEngine.LoadGUIStyleScriptFromFile("tutorials/gui_basics/main-menu.style");

    local parentElement = Game.GetGameWindowGUIElement();
    if parentElement ~= nil then
        self.GUIContainer = GTEngine.CreateGUIElement(parentElement, "main-menu");

        self.StartGameButton = GTEngine.CreateGUIElement(self.GUIContainer, "main-menu-button");
        self.StartGameButton:SetText("Start Game");
        self.StartGameButton:OnPressed(function()
            Game.LoadScene("tutorials/gui_basics/game.gtscene");
        end);

        self.OpenEditorButton = GTEngine.CreateGUIElement(self.GUIContainer, "main-menu-button");
        self.OpenEditorButton:SetText("Open Editor");
        self.OpenEditorButton:OnPressed(function()
            Game.OpenEditor();
        end);

        self.ExitButton = GTEngine.CreateGUIElement(self.GUIContainer, "main-menu-button");
        self.ExitButton:SetText("Exit to Desktop");
        self.ExitButton:OnPressed(function()
            Game.Close();
        end);
    end
end

function self:OnShutdown()
    GTEngine.DeleteGUIElement(self.GUIContainer);
end

Let us run through this code and explain everything. First, you'll see that we're creating the GUI in OnStartup() and deleting it in OnShutdown(). This is usually the best way to create your GUI. You can still dynamically create and delete element in response to any of event and any time, but OnStartup() and OnShutdown() will be the most common.

The first thing we do in OnStartup() is load a style script:

GTEngine.LoadGUIStyleScriptFromFile("tutorials/gui_basics/main-menu.style");

GUI elements are styled using CSS-like style classes. We need to load those classes into the environment before we can use them.

Feel free to change the name and path to whatever you like. Go ahead and create the file if you haven't yet done so and copy the following code:

main-menu
{
    width:            100%
    height:           100%
    background-color: #eee
    horizontal-align: center
    vertical-align:   center
}

main-menu-button
{
    width:      auto
    padding:    8px
    cursor:     hand
    font-size:  16pt
    text-color: #666
}
main-menu-button:hovered
{
    text-color: #333
}
main-menu-button:pushed
{
    text-color: #000
}

If you're familiar with web development (I mean, come on, who isn't?), you'll notice that the styling is very similar to CSS. But don't expect it to be exactly the same. The main thing of note is that a normal style class is not prefixed with a period. Also, handling the layout of elements is a fair bit different (and better). Take a look at the GUI Styling reference page for more information.

Every GUI element should usually have a parent. Every game will have a container GUI that you use as the parent for your own game GUI. You retrieve this like so:

local parentElement = Game.GetGameWindowGUIElement();

To make things easy to delete later on, you'll probably want to create your own container element. When you delete the container, it'll delete the children. We create our own container like so:

self.GUIContainer = GTEngine.CreateGUIElement(parentElement, "main-menu");

The first argument to "Engine.CreateGUIElement()" is the parent element. The second is a space-delimited list of style classes to use with the new element.

After the container has been created, we create our buttons:

self.StartGameButton = GTEngine.CreateGUIElement(self.GUIContainer, "main-menu-button");
self.StartGameButton:SetText("Start Game");
self.StartGameButton:OnPressed(function()
    Game.LoadScene("tutorials/gui_basics/game.gtscene");
end);

self.OpenEditorButton = GTEngine.CreateGUIElement(self.GUIContainer, "main-menu-button");
self.OpenEditorButton:SetText("Open Editor");
self.OpenEditorButton:OnPressed(function()
    Game.OpenEditor();
end);

self.ExitButton = GTEngine.CreateGUIElement(self.GUIContainer, "main-menu-button");
self.ExitButton:SetText("Exit to Desktop");
self.ExitButton:OnPressed(function()
    Game.Close();
end);

It should be easy enough to understand what's going on here. Every GUI element will have OnPressed events posted to it when it is clicked with the mouse.

The last part of the script might be the most important - it deletes the GUI elements that this script created:

function self:OnShutdown()
    GTEngine.DeleteGUIElement(self.GUIContainer);
end

When you delete an element it will delete it's children, so no need to delete each individual button because they are a child of our container.

As an experiment, comment out the delete the statement and see what happens - you should see that the GUI will remain in the viewport even after you have stopped playing the scene. You must delete your GUI elements!

4. Create the Game Scene

When we hit the "Start Game" button, we want to load another scene. Go ahead and create an empty scene called "game.gtscene" (don't include the extension when creating it). There's no need to add any content to the scene, but feel free to do so if you want.

A real game will have an in-game menu like a pause menu, score screen, HUD, etc. You can easily do this in the same way as the main menu.

5. Test

Now go ahead an test the main menu scene by playing it from within the editor. When you hit the "Start Game" button, it should load up the new scene in a new tab and automatically play it.

As a little side note, if you notice that after you stop playing the scene the GUI elements remain in the viewport, it means you haven't deleted them. Remember, you must delete your GUI elements!

6. Conclusion

The purpose of this little "tutorial" is to show the absolute basics of the workflow for creating GUIs in your game. Obviously there is a lot more to explain, but having a basic idea on things should make moving forward a bit easier.

The main takeaway from this is to remember that all GUIs are part of the same GUI environment which means you must delete your GUI elements!