Defold provides you with a custom GUI editor and powerful scripting possibilities that are tailor made for the construction and implementation of user interfaces.
A graphical user interface in Defold is a component that you build and attach to a game object and place in a collection. This component has the following properties:
GUI components are rendered independently of the game view. Because of this it is not placed in a particular location in the collection editor, nor does it have a visual representation in the collection editor. However, GUI components have to reside in a game object that has a location in a collection. Changing that location has no effect on the GUI.
GUI components are created from a GUI scene prototype file (also known as “prefabs” or “blueprints” in other engines). To create a new GUI component, right click a location in the Assets browser and select New ▸ Gui. Type a name for the new GUI file and press Ok.
Defold now automatically opens the file in the GUI scene editor.
The Outline lists all the GUI’s content: it’s list of nodes and any dependencies (see below).
The central editing area shows the GUI. The toolbar in the top right corner of the editing area contains Move, Rotate and Scale tools, as well as a layout selector.
A white rectangle shows the bounds of the currently selected layout, of the default display width and height as set in the project settings.
Selecting the root “Gui” node in the Outline shows the Properties for the GUI component:
Per Node
adjusts each node against the adjusted size of the parent node, or the resized screen.Disable
turns off node adjust mode. This forces all nodes to keep their set size.gui.new_texture()
You can manipulate GUI properties in runtime from a script component using go.get()
and go.set()
:
go.property("mybigfont", resource.font("/assets/mybig.font"))
function init(self)
-- get the font file currently assigned to the font with id 'default'
print(go.get("#gui", "fonts", { key = "default" })) -- /builtins/fonts/default.font
-- set the font with id 'default' to the font file assigned to the resource property 'mybigfont'
go.set("#gui", "fonts", self.mybigfont, { key = "default" })
-- get the new font file assigned to the font with id 'default'
print(go.get("#gui", "fonts", { key = "default" })) -- /assets/mybig.font
end
go.property("myeffect", resource.material("/assets/myeffect.material"))
function init(self)
-- get the material file currently assigned to the material with id 'effect'
print(go.get("#gui", "materials", { key = "effect" })) -- /effect.material
-- set the material id 'effect' to the material file assigned to the resource property 'myeffect'
go.set("#gui", "materials", self.myeffect, { key = "effect" })
-- get the new material file assigned to the material with id 'effect'
print(go.get("#gui", "materials", { key = "effect" })) -- /assets/myeffect.material
end
go.property("mytheme", resource.atlas("/assets/mytheme.atlas"))
function init(self)
-- get the texture file currently assigned to the texture with id 'theme'
print(go.get("#gui", "textures", { key = "theme" })) -- /theme.atlas
-- set the texture with id 'theme' to the texture file assigned to the resource property 'mytheme'
go.set("#gui", "textures", self.mytheme, { key = "theme" })
-- get the new texture file assigned to the texture with id 'theme'
print(go.get("#gui", "textures", { key = "theme" })) -- /assets/mytheme.atlas
end
The resource tree in a Defold game is static so any dependencies that you need for your GUI nodes need to be added to the component. The Outline groups all dependencies by type under “folders”:
To add a new dependency, right click the “Gui” root in the Outline, then select Add ▸ [type] from the popup context menu.
You can also right click on the folder icon for the type you want to add and select Add ▸ [type].
A GUI component is built from a set of nodes. Nodes are simple elements. They can be translated (moved, scaled and rotated) and ordered in parent-child hierarchies either in the editor or at runtime through scripting. The following node types exist:
Add nodes by right-clicking on the Nodes folder and selecting Add ▸ and then Box, Text, Pie, Template or ParticleFx.
You can also press A and select the type you want to add to the GUI.
Each node has an extensive set of properties that control its appearance:
Manual
you can alter the value. The size defines the bounds of the node and is used when doing input picking. This value can be animated from script (learn more).Automatic
the editor will set a size for the node. If set to Manual
you can set the size yourself.gui.pick_node()
. Use gui.set_enabled()
and gui.is_enabled()
to programmatically change and check this property.gui.pick_node()
. Use gui.set_visible()
and gui.get_visible()
to programmatically change and check this property.0
gives no line spacing. 1
(the default) is normal line spacing.Alpha
alpha blends the pixel values of the node with the background. This corresponds to “Normal” blend mode in graphics software.Add
adds the pixel values of the node with the background. This corresponds to “Linear dodge” in some graphics software.Multiply
multiplies the pixel values of the node with the background.Screen
inversely multiplies the pixel values of the node with the background. This corresponds to “Screen” blend mode in graphics software.Possible values are Center
, North
, South
, East
, West
, North West
, North East
, South West
or South East
.
If you change the pivot of a node, the node will be moved so that the new pivot will be at the node’s position. Text nodes are aligned so that Center
sets the text center-aligned, West
sets the text left-aligned and East
sets the text right-aligned.
The following anchoring modes are available:
None
(for both X Anchor and Y Anchor) keeps the node’s position from the center of the parent node or scene, relative to it’s adjusted size.Left
or Right
(X Anchor) scales the horizontal position of the node so it keeps the position from the left and right edges of the parent node or scene at the same percentage.Top
or Bottom
(Y Anchor) scales the vertical position of the node so it keeps the position from the top and bottom edges of the parent node or scene at the same percentage.A node created in a scene where the logical resolution is a typical landscape resolution:
Fitting the scene to a portrait screen causes the scene to be stretched. Each node’s bounding box is similarly stretched. However, by setting the adjust mode, the aspect ratio of the node’s content can be kept intact. The following modes are available:
Fit
scales the node content so that it is equal to the stretched bounding box width or height, whichever is smallest. In other words, the content will fit inside the stretched node bounding box.Zoom
scales the node content so that it is equal to the stretched bounding box width or height, whichever is largest. In other words, the content will fully cover the stretched node bounding box.Stretch
stretches the node content so it fills the stretched node bounding box.If the GUI scene property Adjust Reference is set to Disabled
, this setting will be ignored.
None
renders the node as usual.Stencil
makes the node boundaries define a stencil mask that is used to clip the node’s child nodes.See the GUI clipping manual for details.
The combination of Pivot, Anchors and Adjust Mode properties allows for a very flexible design of GUIs but it can be somewhat hard to understand how it all works without looking at a concrete example. Let’s take this GUI mockup created for a 640x1136 screen as an example:
The UI is created with X and Y Anchors set to None and the Adjust Mode for each node is left at the default value of Fit. The Pivot point for the top panel is North, the pivot for the bottom panel is South and the pivot point for the bars in the top panel are set to West. The rest of the nodes have pivot points set to Center. If we resize the window to make it wider this is what happens:
Now, what if we want the top and bottom bars to always be as wide as the screen? We can change the Adjust Mode for the grey background panels at the top and bottom to Stretch:
This is better. The grey background panels will now always stretch to the width of the window, but the bars in the top panel as well as the two boxes at the bottom aren’t positioned properly. If we want to keep the bars at the top positioned to the left we need to change the X Anchor from None to Left:
That is exactly as we want it for the top panel. The bars in the top panel already had their Pivot points set to West which means that they will position themselves nicely with the left/west edge of the bars (Pivot) anchored to the left edge of the parent panel (X Anchor).
Now, if we set the X Anchor to Left for the box on the left and the X Anchor to Right for the box on the right we get the following result:
This is not quite the expected result. The two boxes should stay as close to the left and right edges as the two bars did in the top panel. The reason for this is that the Pivot point is wrong:
Both boxes have a Pivot point set to Center. What this means is that when the screen becomes wider the center point (the pivot point) of the boxes will stay at the same relative distance from the edges. In the case of the left box it was 17% from the left edge with the original 640x1136 window:
When the screen is resized the center point of the left box remains at the same distance of 17% from the left edge:
If we change the Pivot point from Center to West for the box on the left and to East for the box on the right and reposition the boxes we get the result we’re after even when the screen is resized:
All nodes are rendered in the order they are listed under the “Nodes” folder. The node at the top of the list is drawn first and will thus appear behind every other node. The last node in the list is drawn last, meaning it will appear in front of all other nodes. Altering the Z-value on a node does not control its draw order; however, if you set the Z-value outside of your render script’s render range the node will no longer be rendered to screen. You can override the index ordering of nodes with layers (see below).
Select a node and press Alt + Up/Down to move a node up or down and change its index order.
The draw order can be changed in script:
local bean_node = gui.get_node("bean")
local shield_node = gui.get_node("shield")
if gui.get_index(shield_node) < gui.get_index(bean_node) then
gui.move_above(shield_node, bean_node)
end
A node is made the child of another node by dragging it onto the node that you wish to be the child’s parent. A node with a parent inherits the transform (position, rotation and scale) applied to the parent and relative to the parent pivot.
Parents are drawn before their children. Use layers to change the draw order of parent and child nodes and to optimize the rendering of nodes (see below).
Layers give fine grained control over how nodes are drawn and can be used to reduce the number of draw calls the engine must create to draw a GUI scene. When the engine is about to draw the nodes of a GUI scene, it groups the nodes into draw call batches based on the following conditions:
If a node differs from the previous one on any of these points, it will break the batch and create another draw call. Clipping nodes always break the batch and each stencil scope also breaks the batch.
The ability to arrange nodes in hierarchies makes it easy to group nodes into manageable units. But hierarchies can effectively break batch rendering if you mix different node types:
When the rendering pipeline walks through the list of nodes, it is forced to set up a separate batch for each separate node because the types are different. All in all these three buttons will require six draw calls.
By assigning layers to the nodes, they can be ordered differently, allowing the render pipeline to group the nodes together in fewer draw calls. Start by adding the layers you need to the scene. Right click the “Layers” folder icon in the Outline and select Add ▸ Layer. Mark the new layer and assign it a Name property in the Properties view.
Then set the Layer property on each node to the corresponding layer. The layer drawing order takes precedence over the regular indexed node order, so setting the button graphics box-nodes to “graphics” and the button text nodes to “text” will result in the following draw order:
First all nodes in the “graphics” layer, from the top:
Then all nodes in the “text” layer, from the top:
The nodes can now be batched into two draw calls, instead of six. A major performance win!
Note that a child node with unset layer will implicitly inherit the layer setting of its parent node. Not setting a layer on a node implicitly adds it to the “null” layer, which is drawn before any other layer.
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB