If you’re coding exclusively in Visual Studio Code (VSC) and developing a game in Godot, you’ll need to create and manage UI elements like Labels, Buttons, Health Bars, and Score Counters entirely through GDScript. This guide will walk you through setting up a fully script-driven UI system, ensuring it scales properly and remains organized.

1. Setting Up UI Elements with Code

Since we’re not using the scene editor, we must instantiate UI elements programmatically. UI nodes in Godot fall under the Control class and include elements like:

  • Label – Displays text (e.g., score, notifications).
  • Button – Interactive elements for user input.
  • TextureRect – Displays images/icons.
  • ProgressBar – Represents health, loading, or energy.
  • Containers (VBoxContainer, HBoxContainer) – Helps organize UI automatically.

2. Creating a UI Manager

We need a UIManager script that will create and manage our UI elements dynamically.

Steps:

  1. In your Scripts/UI/ folder, create a new script called UIManager.gd.
  2. Extend CanvasLayer to ensure our UI is drawn above everything else.
  3. Store UI elements in a dictionary so we can reference and update them easily.
extends CanvasLayer

# Dictionary to store UI elements for easy access
var ui_elements := {}  # The ':=' is shorthand for var ui_elements = {}, inferring the type dynamically.

func _ready():
    create_ui()

func create_ui():
    # Create a Label (Score Display)
    var label = Label.new()
    label.text = "Score: 0"
    label.set_position(Vector2(50, 50))
    add_child(label)
    ui_elements["score"] = label  # Store reference for easy access later

    # Create a Button
    var button = Button.new()
    button.text = "Click Me!"
    button.set_position(Vector2(50, 100))
    button.connect("pressed", _on_button_pressed)
    add_child(button)
    ui_elements["button"] = button  # Store reference so we can modify it later

What Does := Do?

In GDScript, := is a shorthand type inference operator. It automatically infers the variable type based on its value.
For example:

var ui_elements := {}  # Infers that this is a Dictionary

This is the same as:

var ui_elements: Dictionary = {}

Using := makes the code cleaner and allows Godot to enforce type safety without explicitly stating it.

Why Store References in a Dictionary?

We store UI elements in ui_elements so we can easily access and modify them later without searching through the scene tree. Instead of using:

var label = get_node("Path/To/Label")

We can just do:

ui_elements["score"].text = "Score: 100"

This keeps our code modular, avoiding hardcoded node paths.


3. Connecting UI Manager to the Game

Right now, UIManager.gd isn’t being used because it isn’t added to the scene. We need to instantiate it in main.gd.

Modify main.gd to Use UIManager.gd:

  1. Open main.gd in your Scripts/ directory.
  2. Instantiate and add the UI manager at runtime.
extends Node

var ui_manager  # Declare variable to store the UI Manager

func _ready():
    ui_manager = load("res://Scripts/UI/UIManager.gd").new()
    add_child(ui_manager)  # Add it to the scene so it renders

Now the UIManager will automatically create and manage UI elements when the game starts.


4. Handling Button Clicks

Let’s make our button update the score label when clicked.

Modify UIManager.gd to Handle Button Clicks:

func _on_button_pressed():
    var label = ui_elements.get("score")
    if label:
        label.text = "Button Clicked!"

Now clicking the button updates the Label dynamically.