The Book of Nodes: UI

Christine Coomans
17 min readAug 7, 2024

--

Below you can find a list of UI nodes that can be used in Godot 4. This is part of my Book of Nodes series. If you want to see similar content on 2D or 3D nodes, please refer to the parent page of this post for those links. 😊

Before we begin, if you need a base project to test these code snippets, feel free to download my FREE 2D and 3D templates here. I’ll be using these templates throughout this post.

*Please note that this list is NOT 100% complete yet, but I will be updating this list as time goes on.

  • Control
  • CanvasLayer
  • CanvasModulate
  • Container
  • HBoxContainer
  • VBoxContainer
  • GridContainer
  • ColorRect
  • Panel
  • Button
  • Label
  • RichTextLabel
  • ProgressBar

Common Properties

Anchoring

  • Determines how a UI element is positioned and resized relative to its parent container.
  • anchor_left: Sets the left anchor point (0 to 1).
  • anchor_top: Sets the top anchor point (0 to 1).
  • anchor_right: Sets the right anchor point (0 to 1).
  • anchor_bottom: Sets the bottom anchor point (0 to 1).
  • full_rect: Sets the anchor point to all sides.

Margins

  • Defines the distance between the UI element and its parent container’s edges.
  • margin_left: Distance from the left edge.
  • margin_top: Distance from the top edge.
  • margin_right: Distance from the right edge.
  • margin_bottom: Distance from the bottom edge.

Font

  • Customizes the appearance of text within UI elements.
  • font: The font resource used for the text.
  • font_size: The size of the font.
  • font_color: The color of the font.

Color

  • Customize the color properties of UI elements.
  • color: The main color of the UI element.
  • font_color: The color of the text.
  • modulate: The alpha value applied to the UI element.

Transform

  • Controls the position, rotation, and scale of UI elements.
  • position: The position of the UI element.
  • rotation: The rotation of the UI element in degrees.
  • scale: The scale of the UI element.

Size Flags

  • Determines how a UI element resizes within its parent container.
  • size_flags_horizontal: Horizontal resizing behavior (e.g., fill, expand).
  • size_flags_vertical: Vertical resizing behavior (e.g., fill, expand).

Visibility

  • Controls the visibility of UI elements.
  • visible: Whether the UI element is visible.
  • self_modulate: The color modulation applied to the UI element, affecting its visibility.

Focus

  • Manages keyboard and controller focus for UI elements.
  • focus_mode: Determines if the UI element can receive focus (none, click, all).
  • focus_neighbour_*: Specifies neighboring UI elements for focus navigation (up, down, left, right).

Tooltip

  • Provides additional information when the user hovers over a UI element.
  • hint_tooltip: The text displayed as a tooltip.

Theme Overrides

  • Allows customization of the UI element’s appearance beyond the default theme.
  • theme: The theme resource applied to the UI element.
  • theme_type_variation: A variation of the theme type for more specific customization.

Control

A Control node provides a bounding rectangle on the viewport that can be used for creating user interfaces in Godot, as they handle input events, focus, and other UI-specific functionalities.

Mechanic:

Drag and drop a sprite on the map.

Implementation:

  • Add a Control node to your scene. This represents the item you will drag and drop.
  • We need to visualize it, so add a TextureRect node to it. This node allows us to add a sprite to the control so that we can see what we are moving. This works better than the Sprite2D/3D node because it can be anchored to our Control container.
  • Add any texture (icon) to this TextureRect.
  • Attach a script to your Control node. To be able to drag and drop this object, we will need to connect its gui_input() signal to our script.
  • In your code, add the functionality to select the item using the LEFT mouse button. Use set_drag_preview to show a visual representation of the item being dragged.
  • Then, add the functionality to drop your item on the LEFT mouse button release.
  • And then also add the functionality to snap the item to your mouse cursor whilst dragging it around.
### Control.gd

extends Control

var drag_offset = Vector2.ZERO
var is_dragging = false

func _on_gui_input(event):
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
# Select
if event.pressed:
is_dragging = true
if get_viewport().gui_is_dragging():
set_drag_preview(self)
else:
# Drop
is_dragging = false
# Drag
elif event is InputEventMouseMotion and is_dragging:
var global_pos = get_global_mouse_position()
global_position = global_pos - size / 2
  • Test your logic by selecting the item and dragging it around.

CanvasLayer

A CanvasLayer is used for independent rendering of objects within a 2D scene, often for UI elements or HUDs. It renders its child nodes independently of the main scene's camera, which means that they remain fixed on the screen.

Example Use Cases:

  • HUD (Heads-Up Display).
  • Fixed-position UI elements.
  • Overlay menus.

Mechanic:

To demonstrate the use of the layer property, we will create a simple scene with two CanvasLayer nodes. One will be used for a background layer, and the other for a UI layer. The background layer will be drawn behind the UI layer.

Implementation:

  • Add two CanvasLayernodes to your scene.
  • Add a Label node to the first CanvasLayer node. Add a ColorRect to the second CanvasLayer node.
  • Give the Label some text, and change the ColorRects color to a dark grey. Also, change its anchor-preset to be full_rect.
  • You will see that our label isn’t showing, because our first CanvasLayer node has the same Layer value as the second CanvasLayer.
  • Change the first CanvasLayer node’s Layer value to be “2”.
  • Your label should now be on top of the background!

Container

A Container is used to organize UI elements, providing a base class for various container types. It inherits from Control and provides functionality for arranging child nodes. We usually don’t use this node a lot, as a normal Control node suffices.

You might, however, use a ScrollContainer node. A ScrollContainer is a type of container node in Godot that provides scrollbars to its child control when needed. It is useful for creating scrollable areas in your UI, such as lists, text areas, or any content that exceeds the visible area of the container.

Example Use Cases:

  • Base class for custom container types.
  • Organizing complex UI layouts.
  • Creating custom layout behaviors.

HBoxContainer

An HBoxContainer is used to arrange UI elements horizontally. It inherits from Container and arranges its children in a horizontal line.

Example Use Cases:

  • Horizontal menus.
  • Toolbars.
  • Row-based layouts.

Mechanic:

Arrange a list of items horizontally on the scene.

Implementation:

  • Add a Control node to your scene. This will hold our HBoxContainer. Change its size to something like 600px by 600px to give it a bounding area.
  • Add a HboxContainer node to this Control node. Also, make sure its anchor preset is full_rect so that it takes up the entire space provided by the Control node.
  • Add three Label nodes to the HBoxContainer.
  • You will see that the items stack next to each other, but they don’t take up the entire space. To fix this, we will need to set their container sizing properties to fill and expand.
  • Now your labels will be arranged horizontally across the entire 600px by 600px boundary box!
  • To add some spacing between the items, select the HBoxContainer node, and change its separation constant to a value such as 10.

VBoxContainer

A VBoxContainer is used to arrange UI elements vertically. It inherits from Container and arranges its children in a vertical line.

Example Use Cases:

  • Vertical settings menus.
  • Stacked buttons.
  • Column-based layouts.

Mechanic:

Arrange a list of items vertically on the scene.

Implementation:

  • Add a Control node to your scene. This will hold our VBoxContainer. Change its size to something like 600px by 600px to give it a bounding area.
  • Add a VboxContainer node to this Control node. Also, make sure its anchor preset is full_rect so that it takes up the entire space provided by the Control node.
  • Add three Label nodes to the VBoxContainer.
  • You will see that the items stack on top of each other, but they don’t take up the entire space. To fix this, we will need to set their container sizing properties to fill and expand.
  • Now your labels will be arranged horizontally across the entire 600px by 600px boundary box!
  • To add some spacing between the items, select the VBoxContainer node, and change its separation constant to a value such as 10.

GridContainer

A GridContainer is used to arrange UI elements in a grid. It inherits from Container and arranges its children in a grid layout.

Example Use Cases:

  • Keypads.
  • Control panels.
  • Inventory grids.

Mechanic:

Arrange a list of items in a grid on the scene.

Implementation:

  • Add a Control node to your scene. This will hold our GridContainer. Change its size to something like 600px by 600px to give it a bounding area.
  • Add a GridContainer node to this Control node. Also, make sure its anchor preset is full_rect so that it takes up the entire space provided by the Control node.
  • Add fourLabel nodes to the GridContainer.
  • You will see that the items stack on top of each other, instead of in a grid. To fix this, we will need to up our columns property in our GridContainer node. Change this value to 2.
  • Now they are arranged in the grid, but they don’t take up the entire space. To fix this, we will need to set their container sizing properties to fill and expand.
  • Now your labels will be arranged in a grid format across the entire 600px by 600px boundary box!
  • To add some spacing between the items, select the GridContainer node, and change its separation constant to a value such as 10.

ColorRect

A ColorRect is used to display a solid color rectangle, often for backgrounds or color overlays. It inherits from Control and provides a simple way to display a rectangle filled with a specified color. The color can be set and changed dynamically.

Example Use Case:

  • Background overlays.
  • Progress bars.
  • Color indicators.

Mechanic:

Add a background color to your scene.

Implementation:

  • Add a CanvasLayer node to your scene. This will ensure that our ColorRect is drawn on our UI, so it can be displayed in the background.
  • Add a ColorRectnode to your CanvasLayer node.
  • Change its color, and change its anchor preset to be full_rect so that it takes up the entire screen.
  • Change the CanvasLayer node’s layer property to something like -1 so that it is displayed behind our other nodes. We should now have a background color!

Panel

  • A Panel is used to create a panel for grouping UI elements, providing a background and border. It inherits from Control and provides a simple way to group and visually separate UI elements.

Example Use Case:

  • Settings panels.
  • Dialog boxes.
  • Grouping related UI elements.

Mechanic:

Add a background color to your scene.

Implementation:

  • Add a CanvasLayer node to your scene. This will ensure that our Panel is drawn on our UI, so it can be displayed in the background.
  • Add a Panel node to your CanvasLayer node.
  • To change its color, we’ll need to give it a new theme style of type StyleBoxFlat.
  • Now we can give it a new color, border, and even a shadow!
  • Change its anchor preset to full_rect so that it takes up the entire screen.
  • Change the CanvasLayer node’s layer property to something like -1 so that it is displayed behind our other nodes. We should now have a background color!

CanvasModulate

A CanvasModulate applies a color tint to all nodes on a canvas. It tints the canvas elements using its assigned color.

Example Use Cases:

  • Night mode effect.
  • Global color adjustments.
  • Visual effects for different game states.

Mechanic:

Add a day-and-night cycle to your game

Implementation:

  • Add a CanvasModulatenode to your scene.
  • If you change its color property, the entire scene's color should change.
  • We want to animate this property. So, add an AnimationPlayer node to your scene.
  • Create a new animation called day_night_cycle. Set the length of this animation to be 24 seconds long. This will make our day 24 seconds long (for 24 hours).
  • Also, enable looping.
  • Now add a color keyframe for every 6 hours (or whatever preference you have). These colors should represent the colors from 0 AM to 24 PM.
  • On my timeline, my colors are:
    0: #0f0a49
    6: #7b5436
    12: #ffffff
    18: #5b6a99
    24: #0f0a49
  • Now when we run our game, we should play this animation.
### Main.gd

extends Node2D

@onready var animation_player = $AnimationPlayer

func _ready():
animation_player.play("day_night_cycle")
  • If you want a better tutorial using shaders, check out the video on my YouTube channel!

Button

A Button is used to create a clickable button for user interactions. It inherits from BaseButton and provides functionality for detecting clicks and triggering actions.

If you want to use a button that has a texture (image) and more customizability, you should use a TextureButton node. The TextureButton node is a button that uses textures for its different states (normal, pressed, hover, etc.) instead of the default theme. This allows for more customized and visually appealing buttons. It inherits from BaseButton and provides properties to set textures for different states.

Example Use Cases:

  • Interactive buttons.
  • Menu options.
  • Form submissions.

Mechanic:

Add a clickable button to your scene.

Implementation:

  • Add a CanvasLayer node to your scene. This will ensure that our Buttonis drawn on our UI, so it can be displayed at all times.
  • Add a Button node to your CanvasLayer node.
  • If you run your scene, your Button should be visible.
  • Now, attach its pressed() signal function to your script.
  • If we press the button, we will change its text. We will keep track of the times we’ve pressed it and update it each time we press the button.
### Main.gd

extends Node2D

@onready var button = $CanvasLayer/Button

var button_pressed_count = 0

func _on_button_pressed():
button_pressed_count += 1
button.text = "You've pressed me: " + str(button_pressed_count) + " times!"
  • Every time you press the button, the count should update!

Label

A Label is used to display text. It inherits from Control and provides functionality for displaying text.

Example Use Cases:

  • Static text display.
  • Descriptive labels.
  • Titles and headings.

Mechanic:

Add a label that changes its text when you hover over it.

Implementation:

  • Add a CanvasLayer node to your scene. This will ensure that our Label is drawn on our UI, so it can be displayed at all times.
  • Add a Labelnode to your CanvasLayer node.
  • If you run your scene, your Label should be visible.
  • Now, let’s make it more visible. Change its font color to be red, and its size to something like 25px.
  • Since we want it to detect mouse events, we need to change its Mouse Filtermode to Pass.
  • Now, to detect that we are hovering over it, we need to attach its mouse_entered() and mouse_exited() signals to our script.
  • Then, simply change the text when we enter and exit the label while hovering! If you run your game, the label should change depending on whether or not you are hovering over it.
### Main.gd

extends Node2D

@onready var label = $CanvasLayer/Label


func _on_label_mouse_entered():
label.text = "You're hovering over a label"


func _on_label_mouse_exited():
label.text = "You're away from the label"

RichTextLabel

A RichTextLabel is used to display formatted text. It inherits from Control and provides functionality for displaying rich text with formatting, such as bold, italics, and images.

Example Use Cases:

  • Formatted text display.
  • Dialogue boxes.
  • Instructional text.

Mechanic:

Write words in a typewriter effect.

Implementation:

  • Add a CanvasLayer node to your scene. This will ensure that our RichTextLabelis drawn on our UI, so it can be displayed at all times.
  • Add a RichTextLabel node to your CanvasLayer node.
  • Change its size to something like 200px by 200px.
  • If you run your scene, your RichTextLabel should be visible.
  • Now, let’s make it more visible. Change its font color to red, and its size to something like 25px.
  • To create our typewriter animation, we will need to use an AnimationPlayer node. Add this node to your scene.
  • Create a new animation called typewriter.
  • To create a typewriter effect, we need to animate our RichTextLabel node’s visible ratio property, which defines how much of our text is visible.
  • At the 0 keyframe mark, set your visible ratio to be equal to 0.
  • At the 1 keyframe mark, set your visible ratio to be equal to 1. This means in 1 second, our text will go from no characters to full characters.
  • Now if you play your animation, your text should play as if it's being typed out.

ProgressBar

ProgressBar is a UI element that visually represents a value within a range. It inherits from the Range class, which provides properties like min_value, max_value, and value to control the progress.

Example Use Cases:

  • Loading screens.
  • Health bars.
  • Progress indicators.

Mechanic:

Create a health bar that increases every 1 second, and when we press SPACE, it decreases in value.

Implementation:

  • Add a CanvasLayer node to your scene. This will ensure that our ProgressBar node is drawn on our UI, so it can be displayed at all times.
  • Add a ProgressBarnode to your CanvasLayer node.
  • Change its size to something like 200px by 27px, and enable its Rounded value so it only shows whole numbers.
  • To be able to increase our ProgressBar, we should add a Timer node. Enable this timer node’s autostart property, and connect its timeout signal to your script.
  • Whenever it times out (every 1 second its active), we will increase our “health” value.
### Main.gd

extends Node2D

@onready var progress_bar = $CanvasLayer/ProgressBar

func _on_timer_timeout():
progress_bar.value += 1
  • Whenever we press our SPACE bar, we should decrease our health value.
### Main.gd

extends Node2D

@onready var progress_bar = $CanvasLayer/ProgressBar

func _on_timer_timeout():
progress_bar.value += 1

func _input(event):
if event.is_action_pressed("ui_accept"):
progress_bar.value -= 5
  • Now if we run our scene, our health should increase every second, but decrease when we press our button.

--

--

Christine Coomans
Christine Coomans

Written by Christine Coomans

Just a *redacted* trying to human. 👾Visit my website for more cool resources: https://christinecdevs.site