What You’ll Learn in This Hour:
Displaying text inside the game window
Important Control nodes properties and signals
Using Containers to help create an interface
Using GDScript to make an interactive interface
Applying a Theme to your controls
As a game developer, communicating with players is almost unavoidable, whether you need to tell the players how many lives they have
currently, how many items they possess, what have storytellers been saying to them, where they are, where should they go next, and so on. The
same could be said for players. They need to tell the game what weapon they would like to use, which difficulty level they prefer, etc. This
communication happens as soon as the game is launched and is done through the user interface.
In this hour, you’ll learn how to use nodes designed specifically for user interface, Control. We will start with one of the most basic Control
nodes, Label, by using it to display text on screen. Then, we get to know some useful properties of Control nodes. We’ll also introduce you to
Control nodes that are frequently used. Next, you’ll learn about Container nodes and experiment with building a complex interface with them.
You’ll also learn how to make great interfaces using Theme. If you can’t find a specific Control ready to be used right out-of-the-box, you can
make your own basic Control.
Control
Control is a node inherited from CanvasItem, which means that it can be drawn in 2D. You might remember another class that also inherits
from CanvasItem: Node2D. The main difference between these two is Control has a rectangular boundary (rect) that contains and sometimes
crops its content, while Node2D has no such boundary. This is one of the most common concepts that confuses starters because these
differences might not be obvious. Control sizing is relative to the parent’s boundary, similar to Node2D, which inherits the parent’s transform.
Basic Control Nodes: Label and ColorRect
Let’s begin by creating a Label. It’s a simple Control that displays text inside a game window. Creating a Label is done the same way you
create other nodes. Label directly inherits from Control (see Figure 9.1
)
.
FIGURE 9.1
A newly created Label node.
When you’re done adding the Label, give it some text so that you can visualize it better. This can be done in the Inspector (see Figure 9-2).
FIGURE 9.2
Editing text property of a label using the Inspector.
If you want to add text that spans multiple lines, click the paragraph button next to the text box. A dialog with a larger text area will appear
(Figure 9.3).
FIGURE 9.3
A label with text.
When a Control is selected, it is highlighted by its boundary (see Figure 9.3). You can drag any pink dot to resize the Control. However, you
may notice you cannot resize the Label to be smaller than the space occupied by text. This is the absolute minimum size of a Control. Controls
will never have a smaller size than this value. Minimum size varies by Control and its content. For Labels that don’t clip text, their minimum size
are the area where the text resides.
Let’s add another Control node as a background of Label. Create a new ColorRect as a child of Label (see Figure 9.4). A white-colored
rectangle should overlap where the Label is. Set its color property to be whatever you like, preferably not the same as Label’s text color
because we are going to make it a background. Then, go to the CanvasItem section and check the show_behind_parent property, which
does exactly what it says. Our Label should visually be on top of the ColorRect now.
FIGURE 9.4
Show behind parent property makes a Label visible on top of a ColorRect.
We will make this ColorRect’s boundary match that of the Label. Select ColorRect node, go to the 2D Editor toolbar, look for “Anchor,” click it,
and choose “Full Rect and Fit Parent” at the bottom (see Figure 9.5).
111111111
22222222FIGURE 9.5
“Full Rect and Fit Parent” inside the Anchor menu.
The ColorRect should be the Label’s background now (see Figure 9.6).
FIGURE 9.6
A Label with ColorRect background.
Properties of Control
We will use the previous Label to introduce you some of the important properties of Control. This is applicable to almost every Control node.
To follow along, select a Label node and look at the Inspector. Scroll down a bit until the Control section is visible (see Figure 9.7).
FIGURE 9.7
“Control” section in the Inspector.
We will take a look at each one in the following sections.
Anchor and Margin
Anchor and Margin both have four subitems: Left, Top, Right, and Bottom. They are related and affect each other. Let’s look at Margin first
(Figure 9.8).
FIGURE 9.8
Effect of Margin property to a Control.
Margin is a distance in pixels relative to the parent’s boundary. Currently, our Label has Left and Top set to zero. Try changing Left to 15 and
Top to 30 and then look at the difference.
You will see the position has changed according to the Margin value. F
r
om Figure 9.8, Label is moved 15 pixels to the right and 30 pixels
lower. If you set Margin to be a negative value, it will move the Control the other direction.
Anchor affects how Margin is calculated. Currently, all of our Label Anchors are set to “Begin,” which means that Margins are relative
horizontally to the parent’s Left and vertically to the parent’s Top (see Figure 9.9).
FIGURE 9.9
How Margins are calculated relative to Anchor. (Right Anchor set to “Begin.”)
Let’s try setting Right Anchor to “End.” This will make our Right Margin relative to the parent’s Right (see Figure 9.10).
FIGURE 9.10
How Margins are calculated relative to Anchor. (Right Anchor set to “End”) Notice an unchanged boundary and difference in Margin value.
Anchor also dictates how the Control is resized if the parent is (see Figure 9.11) because Controls try to keep the Margin constant.
FIGURE 9.11
Resizing the parent. (Right Anchor set to “End”) Notice an expanded boundary and constant Margin value.
Try experimenting with other sides too (Control’s Left, Top, and Bottom). You can also use the “Anchor” menu to set Anchors and Margins
automatically.
Rect
This also contains properties related to boundary and sizing. The transformation similar to that of Node2D’s can be found here. Let’s look at
each one:
Position: location of the Control relative to its parent.
Size: size of the Control’s boundary.
Min Size: how small the Control can be resized. This has no effect if it’s smaller than the intrinsic Control minimum size. You might want
to set this property manually to keep an interface visible and readable.
Rotation: Control’s rotation around the pivot in degrees. Positive is clockwise.
111111111
22222222Scale: Control’s scale, expanding or shrinking from the pivot.
Pivot Offset: location of the pivot relative to the upper-left corner.
Clip Content: whether e content outside the boundary is drawn.
Hint
This is the text that appears when players hover their mouse cursor on it for a while. It can be used to tell additional information related to the
Control, e.g. contents that are too long to be displayed, the purpose of the Control, etc.
Focus
This lets you define nearby Controls manually. Note the engine can find another Control automatically when players press tab to focus on the
next Control. This option lets you override it in case it chooses the wrong Control node.
Size Flags
This contains properties related to sizing inside Containers. Flags vary by Container types. In general:
Fill: Fills the Container along that axis.
Expand: The Control takes all available space but won’t fill it with content. (as a spacer).
Fill and Expand: Takes all available space and fills, it with content.
Shrink Center: The Control keeps itself small and adjusts itself in the center.
Shrink End: The Control keeps itself small in the right side or bottom.
Stretch Ratio: The Control’s size is relative to that of its siblings. Imagine two Controls being siblings, one with a Stretch Ratio of “2” and
another with “1”. The one with a Stretch Ratio “2” has the size doubled of another Control.
Theme
Theme is a resource that contains information related to panel drawing. It also contains predefined values for Controls, such as default margin,
size, color, and font, etc. If a Control is not given its own Theme, it will inherit its Theme from its parent, resulting in an interface with a uniform
look. Theme is discussed later this hour.
Custom Values
This lets you override values defined in Theme individually, in case the predefined ones don’t look good in a specific setting. For our Label,
you can set a new font, font size, shadow color, shadow offsets, and so
on. Try giving our Label a shadow by selecting a check box in front of
the “Font Color Shadow” property.
Signals of Control
Like most of the objects of Godot Engine, Control also has signals you can use to execute functions. The following is a nonexhaustive list of
signals from the base Control class only. There are more signals available in each specific Control.
focus_entered()/focus_exited(): called when Control has or loses focus
gui_input(InputEvent ev): called when input event happens inside Control area or while Control has focus for key input
mouse_entered()/mouse_exited(): called when mouse cursor enters or exits the boundary
resized(): called whenever the size is changed
Know Your Controls
There are many Controls bundled with the engine. In this topic, we will get to know some that are used often in game interfaces.
Label
Label is a Control that displays text. Usually, players cannot interact with this Control because it’s used only as a display. Here are some of its
important properties:
text: a displayed String.
align and valign: text alignment in horizontal and vertical axes.
autowrap: allows resizing, wrap lines that are longer than the width, and adjusts height automatically.
clip_text: allow resizing, but won’t automatically adjust the height. As a result, displayed text will be truncated to the Control’s height.
RichTextLabel
This is a more advanced label that supports styling via BBCode. Edit the text property to use. It’s similar to normal Labels.
Text Inputs
Sometimes you want to get text from players. One example is letting players type their character’s name using a keyboard. Following are the
Controls that provide a text area in which players can type.
111111111
22222222LineEdit
LineEdit is a single-line text area. Here are some of its interesting properties:
text: text inside the LineEdit that can be edited by players.
editable: whether players are able to type in the text area.
secret: whether to display text as asterisks. () This is useful when making a password input box.
placeholder_text: text that is displayed when the box is empty. It can be used as a hint of what players should enter, for example,
“Player name,” “Host IP,” etc.
TextEdit
TextEdit is a multiple line text area. You may use set_text(String text) or get_text() to change and retrieve text inside.
Range
Sometimes you also want to get numeric data from players. In games where characters have hit points, you may want to display it using a
gauge interface so that it’s easier to understand, and it looks better than plain numbers. In games with a trading feature, you might want to
know how many items they are going to buy or sell from merchants. This topic discusses Controls that help you get numbers from players while
providing an easy-to-use interface. But before that, let’s look at some properties these Controls have in common.
min_value/max_value: minimum and maximum value.
value: the current value held by the Control. It will not be below min_value and exceed max_value.
step: a constant number that may be added or subtracted to value.
ProgressBar
Also known as a “loading bar,” the ProgressBar is a horizontal control that shows progress as it’s being filled with color. Usually, players cannot
interact with this Control.
TextureProgress
A more customizable variation of ProgressBar. TextureProgress lets you replace the bar with images. A circular progress bar is also
supported.
Slider
Slider is a Control with a dragable button that changes the value.
SpinBox
This is a Control similar to what you’ve used in the Inspector. It changes the value by clicking the up and down arrows or by clicking and
dragging the arrow up or down.
BaseButton
Buttons are useful when you want to know how users want to proceed, whether the form is submitted or discarded, which menu is shown, and
so on. Since BaseButton is an abstract class that all buttons are inherited from, it is important to know some of its properties and signals.
Some of its interesting properties are listed below:
disabled: decides whether users can interact with this button.
toggle_mode: decides whether the button can stay pressed.
pressed: the current state of the button. It’s mostly useful with toggle_mode on.
shortcut: an input event that triggers the button’s action.
group: only one of all buttons in the same group can be selected. This is mostly useful with toggle_mode on.
Useful signals of BaseButton are listed below:
button_up()/button_down(): called when the button is released or pressed.
pressed(): called when the button executes an action, which by default, is when it is unpressed. It can be configured to emit when being
pressed by setting the action_mode property. However, the signal will be fired before the player has a chance to cancel the action.
toggled(bool pressed): called when the button executes an action while toggle_mode is on.
Button
A simple button has a custom caption and an optional icon.
TextureButton
This is a more customizable variation of Button that uses images. You’ll need “normal,” “pressed,” “hovered,” “disabled,” and “focused”
textures.
CheckBox and CheckButton
111111111
22222222These buttons are set to toggle_mode on by default. CheckBox shows check marks inside a box when pressed. CheckButton looks like a
switch and displays “ON” when pressed and “OFF” otherwise.
OptionButton
OptionButton is a dropdown menu that lets users select an item.
ColorPickerButton
ColorPickerButton is a button that shows a color picker when clicked. A chosen color is kept as a property. You can use this Control to easily
create customization settings for player characters, for example. The color_changed signal is emitted when a new color is chosen.
Popup
Popup is a window that displays on top of other Controls. It usually tells users an action is required for another action to complete. Popups are
hidden during runtime. To display a Popup, use any of the popup() methods.
PopupPanel
PopupPanel is one of the simpler types of Popups as it only has a panel as a background and nothing else.
PopupMenu
PopupMenu displays selectable items. It will emit id_pressed and index_pressed signals upon selection.
WindowDialog
WindowDialog is a PopupDialog with a customizable title bar. It can be set to be resizable.
AcceptDialog
AcceptDialog is a WindowDialog with an accept button captioned “OK” by default. Confirmed signal is called upon a button press.
ConfirmationDialog
ConfirmationDialog is an AcceptDialog with a cancel button.
FileDialog
FileDialog is a complex dialog that is used to browse for files and folders. Interesting properties are listed below:
mode: the dialog’s access mode. The dialog configures its interface to match this setting.
access: the file path to which the dialog has access. Note that the resource path usually isn’t accessed for writing at runtime, only
reading.
filters: only files with one of these extensions is displayed.
show_hidden_files: whether hidden files and folders are displayed.
The following lists all signals of FileDialog:
dir_selected(String dir): called when a folder has been selected
file_selected(String path): called when a file has been chosen
files_selected(PoolStringArray paths): called when files are selected while the dialog is set to “Open Many” mode
ItemList
ItemList displays items, which can be text or icon, in a nice-looking table. One example is the lower part of the editor’s FileSystem dock. Items
are interacted with and signals are emitted accordingly.
TextureRect
A TextureRect displays an image constrained by its boundary. Image can be set to tiling mode, scaling to fit or fill the area, or fixed in the
center using the stretch_mode property.
ColorRect
This is a simple Control that displays nothing but color inside the boundary. Use the color property to set the color.
NinePatchRect
This is an image-displaying Control with unique scaling. The image used should have “corners,” “sides,” and “center.” The Corners size is
constant. Sides are expanded or tiled in one axis until they meet the other end. The center is expanded or tiled in both axes to fill the middle
part. This is useful when making a panel using a custom texture.
Containers
The Anchor and Margin system can be a bit difficult to use and it takes time to achieve the desired outcome, especially when dealing with
111111111
22222222multiple Controls in the same level. Containers are designed for this situation. They are used as a parent for Controls that are in the same level,
because they automatically adjust their child nodes. Note some Container types squeeze the children into their minimum size unless they are
set with the appropriate size flags or given a custom minimum size.
BoxContainer
BoxContainer is useful when you want a child Control arranged in a row or column. For HBoxContainer, the first child Control will appear on the
left and subsequent children will go to its right in order. It’s the same thing with VBoxContainer, but working from top to bottom.
SplitContainer
SplitContainer contains two children separated by a drag-able bar that resizes both children.
MarginContainer
This is a simple Container that gives its child a custom margin. Note these margin values are located in the “Custom Constants,” not “Margin.”
ScrollContainer
This Container type automatically shows scroll bars when its child is larger than itself.
TabContainer
TabContainer, despite acting similar to other Containers, is not listed as a Container type. It puts its children in a tabbed interface, shows the
one selected, and hides the others. Note its children can be of any CanvasItem type and are not limited to “Tabs” as most starters mistakenly
assume from the name.
Making an Interface
Let’s make an interactive interface using some of the Controls from previous topics. We will create an AcceptDialog that displays what we’ve
typed in a LineEdit. Because there is a Popup in the interface, we will set the root node as a generic Control node to keep the Scene Tree
structure in order. Let’s begin by adding nodes to the Scene Tree as follows in Figure 9.12.
FIGURE 9.12
A simple interface.
The interface preview in the 2D editor looks a bit messy (Figure 9.13).
D
on’t worry about it. It can be fixed easily as long as the tree is in order.
FIGURE 9.13
An unfinished interface.
As you’ve learned from previous topics, Control size is relative to the parent’s boundary. To make use of all available space easily, we will set
the root Control to fill the whole screen using “Full Rect and Fit Parent” in the Anchor menu (see Figure 9.14).
FIGURE 9.14
An unfinished interface.
The changes aren’t visible because the child nodes with contents aren’t affected. Select the HBoxContainer node and do the same (see
Figure 9.15).
FIGURE 9.15
An unfinished interface.
The LineEdit and Button appear in a vertical shape because of the size flags. Select both Controls and change the vertical size flag to none.
Then select the LineEdit node and set the horizontal size flag to fill and expand (see Figure 9.16).
FIGURE 9.16
An unfinished interface.
The interface now looks a little more like what we want. Add a placeholder and a caption for both Controls (Figure 9.17).
FIGURE 9.17
A simple interface.
111111111
22222222All that is left is coding the logic. You can attach a script to any node as long as the node paths are correct. This example in Listing 9.1 uses the
root node as a script Container. Create and attach a new GDScript to the root node.
LISTING9.1 Script for the Root Control Node
Click here to view code image
extends Control
func _ready():
$HBoxContainer/Button.connect(“pressed”, self, “button_pressed”)
$HBoxContainer/LineEdit.connect(“text_entered”, self, “show_dialog”)
func show_dialog(name):
if name == “”: name = “anonymous”
$AcceptDialog.dialog_text = “You are %s.” % name
$AcceptDialog.popup()
func button_pressed():
show_dialog($HBoxContainer/LineEdit.text)
We can’t connect both signals to the same function, because they have a different number of parameters. Now save the scene and run it. Type
some text, then press the button or enter key (see Figure 9.18).
FIGURE 9.18
AcceptDialog shows the text inside LineEdit.
The interface should now work as expected. Also, thanks to the HBoxContainer, the scene should be responsive to window resizing.
Theme
It is understandable to want to give your game a unique look and feel. The default user interface theme might not match your game aesthetic.
Godot Engine provides an easy way to customize the look while keeping it consistent throughout the components. To create a new Theme, go
to the Inspector and click the “Create new Resource” button. Find “Theme” resource in the list, choose it, and click “Create.” The bottom panel
will have a “Theme” option available (see Figure 9.19).
FIGURE 9.19
Theme configuration panel.
To add a new entry to the Theme, click the upper-left menu button that says “Theme,” and choose “Add Item” as shown in Figure 9.20.
FIGURE 9.20
Theme configuration menu.
A dialog will appear. Click the “..” button to show the list of available items or manually type an option in the box (Figure 9.21).
FIGURE 9.21
Add Theme Item dialog.
You can find the names and the data type of a Control in the documentation of that Control in the GUI Theme Items section (see Figure 9.22).
FIGURE 9.22
GUI Theme Item section inGodot Engine documentation.
Click “Add” to add it to the Theme resource (see Figure 9.23).
FIGURE 9.23
New Theme Item and preview.
The preview will show how it looks, as shown in Figure 9.24.
FIGURE 9.24
The preview.
111111111
22222222StyleBox
StyleBox is also a resource and dictates how panels, boxes, and the like are drawn. There are three types of StyleBox:
StyleBoxEmpty: draws nothing
StyleBoxFlat: draws a single color
StyleBoxTexture: draws an image similar to a NinePatchRect
We’ll try adding a new StyleBox to a Normal button. Choose “Theme,” then “Add Item.” Pick “Type: Button,” “Name: normal,” “Data Type: Style,”
then click Add. Click on the new Item, “
icon as Texture, set all margins to “4,” and set modulate color to R:30, G:30, B:30, and A:255 to darken it (see Figure 9.25).
FIGURE 9.25
A configured StyleBoxTexture.
Go back to the Theme resource by clicking “<” icon in the Inspector. The buttons are replaced by the Robot icon. Note that this only affects the
“normal” state of buttons (see Figure 9.26).
FIGURE 9.26
Button with custom StyleBoxTexture.
Save the Theme using the “Save Resource” button and then apply it with the root Control of the scene from the previous topic (Figure 9.27).
FIGURE 9.27
Button with custom StyleBoxTexture.
You may notice the button’s appearance changed despite the Theme being put in the root Control and not the button node. This is because
child Controls inherit their parent’s Theme if they aren’t given one.
Try using StyleBoxEmpty and StyleBoxFlat with other types of Control.
Custom Control
Controls bundled with the engine should fulfill most use cases. But what if you can’t find what you want? In this final topic of this hour, we will
learn how to make a control that doesn’t come with the engine. We’ll make a ColorRect that changes color according to the cursor position
inside it.
The first thing to consider is, “Is there any similar Control to inherit from?” For our example, it is ColorRect. But if there’s none, you can always
inherit from a generic Control node. Now create a ColorRect node, set it to “Full Rect and Fit Parent,” and attach the following GDScript shown
in Listing 9.2.
LISTING9.2 Script for a CustomControl Inheriting fromColorRect
Click here to view code image
extends ColorRect
func _gui_input(ev):
if ev is InputEventMouse:
var v = ev.position/rect_size
color = Color(v.x, v.y, 1, 1)
The _gui_input works like _input but only when the input event happens inside the boundary or the Control is focused. We use this callback to
change the color property of the Control.
Save the scene and give it a meaningful name. To use this custom Control, simply instance that scene. You can instance it multiple times in the
same scene because the properties won’t interfere with one another.
Summary
In this hour, you learned about important Controls, their properties, and signals. You learned about Containers and how they can help build an
interface. You’ve tried making an interface using various Controls, such as LineEdit, Button, HBoxContainer, AcceptDialog, ColorRect, etc.
You’ve made a custom Theme and applied it with a Control. And finally, you’ve created a custom Control that makes use of the _gui_input
callback.
Q&A
Q. Can I use drag-and-drop with Controls?
A. Yes, there are methods for handling dropped data. Drag-and-drop is one of the more advanced implementations of Control. Godot Engine
Demo repository offers a demo on this feature.
111111111
22222222Q. Can I create customContainers using GDScript?
A. Yes, by inheriting Container and handling the sort_children signal.
Q. Can I use Controls in 3D?
A. Controls are 2D-node types. To use them in 3D, you may put them in a Viewport and display as ViewportTexture. To make them
responsive to input events, you can raycast from the root Viewport, and then pass an input event to the UIViewport using input().
Workshop
Answer the following questions to make sure you understand the content of this hour.
Quiz
1. True or False: Controls can be rotated.
2. If you want a Control to stick with the parent’s bottom-right corner, what are the Anchor settings.
3. True or False: Margin values cannot be negative.
4. True or False: You are limited with predefined names for Theme Items.
5. True or False: Controls can be created byGDScript.
Answers
1. True. Controls can be rotated using rotation property or inheriting the parent’s transform.
2. All Anchors are set to “End.”
3. False. Negative Margin value moves the Control in the opposite direction.
4. False. You can use any name. A custom-named Theme Item will be ignored by the engine, but you can refer to it using GDScript.
5. True. Almost every one of Godot Engine’s objects can be created withGDScript. Because Controls are nodes, they need to be added to
the SceneTree using add_child to be operational.
Exercises
Try to execute the following exercises to get more acquainted with the user interface inGodot Engine:
1. Make a simple Music Player using AudioStreamPlayer node with “Open,” “Play/Pause,” and “Rewind” buttons.
2. Choose any application on your machine and try to replicate its interface inside Godot Engine. You don’t have to include all of its
functionality, but make sure the interface can handle window resizing.
3. Customize parts of the engine using Theme. The Editor Theme can be set in Editor menu > Editor Settings > Interface > Theme >
CustomTheme.