Drawing our interface

To draw our interface, we need to add it to a place that draws stuff. A prime example of this is the RenderWidgets function of a gamemode. But first, we need to instantiate a Widget Hoster which will do most of the hard work of interfaces for us. Here's how we would load the hello.gui file inside of a gamemode:

More about the gui file

Gui files are formatted as basic XML. Here, the gui tag is our root element, and is required for all interfaces. Inside of this, we have the doc tag, which is also required. Inside of that, we have our first widget, which will be the root widget. In this case, we use the GroupWidget by using the group tag. A group widget does not draw anything to the screen. Instead, it is simply a container. It has the special property that it will automatically take the width and height of its parent widget, or if there is no parent widget, the entire screen. This makes it perfect as a container for all other widgets.

Inside of that, we have our first real widget, a TextWidget, which we indicate by using the text tag. This widget is inside of the group tag, which means that this text widget is a child of the group widget, and the group widget is its parent. Also note that we close the text tag immediately instead of opening it, since we don't need to have any child widgets inside of it.

The font and text attributes speak for themselves, however the anchor attribute in the above example is more interesting. This is a vector attribute, meaning it accepts 2 floating point numbers. The first is X, the second is Y. These values defines the scalar position of the widget within its parent's size (based on its own size). For example, if you set the anchor X to 0, the widget will be left-aligned. (0 is the default for both X and Y if you don't define the anchor.) If you se this value to 0.5, it will be aligned in the center of its parent. And if you set it to 1, it will be aligned on the right side. The same counts for Y, but then for the Y axis, where 0 would be the top and 1 would be the bottom. Thus, in our example above, 0.5 0.5 means to put the widget exactly in the center.

Sprites

Any sprites you want to display in your interface need to go into a sprites tag. You can then display them with the sprite widget. Sprites are defined globally in your .gui file, outside of the doc tag, like this:

Here, we take the "globe" sprite and put it in a sprite widget just above the text we created in the previous section. The globe sprite's frame is inside of the gui/icons.png texture, where it is located at X 26 and Y 44. It also has a width and height of 13. This example looks like this:

We can also define animating sprites, by adding more frames and an additional time:

Adding interaction

By default, interface interaction is disabled. You have to do the following to be able to interact with an interface:

1. Place an interactable widget, such as a button.
2. Make sure you are calling Update on the widget hoster and that you have added it to the list of widget roots.
3. Additionally, inherit from IWidgetHoster so you can catch events in OnFunc.

Let's go through this step by step.

Placing a button

To place a button, the most convenient way is using the scalebutton widget. An example would be this:

Note the spriteset here is set to scalebutton. This means it will load several sprites prefixed by the string scalebutton. A full list of this is:

scalebutton-left

scalebutton-mid

scalebutton-right

scalebutton-hover-left

scalebutton-hover-mid

scalebutton-hover-right

scalebutton-down-left

scalebutton-down-mid

scalebutton-down-right

Fortunately, you don't have to define these sprites yourself by hand. You can use the default %include to automatically add all of these (including a disabled variation) to your .gui file. To use this, add the following line inside of your sprites tag:

%include "gui/main_menu/scalablebutton_sprites.inc"

If you save this and start the game now, you should see a nice blue button like this:

Widget roots

You'll notice that the button can't actually be clicked or hovered over. That's because the widget hoster is not configured to receive any user input, it's only being drawn to the screen. We can solve this by calling the gamemode's AddWidgetRoot function somewhere. For example, directly in our Start function:

If you start your level now, you will be able to hover and click the button, but you will also be shooting your weapon and you can still walk around. To work around this, you can override ShouldFreezeControls and return true when your interface has input focus. You'll have to work with some boolean, and do something like:

In the next step we will put this boolean inside of its own class to clean up a bit.

Inheriting from IWidgetHoster

For the sake of simplicity, this guide started by having you instantiate a IWidgetHoster directly. This is however not best practice in most cases, plus doing that won't let you catch events from user interaction on the interface. It also doesn't encapsulate everything very well, see for example that ugly loose m_isInterfaceVisible boolean inside of our gamemode, which is less than ideal.

The way interfaces are generally built in scripts, is by inheriting IWidgetHoster and putting all interface logic inside of your class. The following example encapsulates our gui/hello.gui logic inside of a new HelloGui class that is in itself a widget hoster. For this example I've also added a few helpful things to this compared to the basic example above:

Loading the .gui (with filename) directly in the constructor of our class.

Convenient Show and Hide functions which will help us to easily toggle the visibility and interactability of the interface.

A visibility boolean that we use to stop rendering and updating of the interface when hidden.

Catching events

Events in interfaces are simple strings sent to the OnFunc function of a widget hoster. To get an event when the user clicks the "Click me" button we made in the example above, we will need to add the func attribute to the widget, like: func="click_me_was_clicked"