An Overview of GDScript, the Scripting Language for Godot

February 01, 2025

A way to get ready for learning the programming language that can help speed up game development.

godot gdscript
1

In the world of video game creation, where technology brings ideas to life, we need an easy-to-use tool to help us make it happen. GDScript is a special programming language designed for the Godot Engine. It helps developers turn their creative ideas into real games without too much effort.

Godot game engine

GDScript Syntax Basics

Note: While we'll use the _ready function for our examples, remember that Godot offers various other built-in functions for different purposes, such as _process for frame-by-frame updates. This blog post will discuss additional built-in functions and their applications in subsequent sections.

Variables and Data Types
Booleans

Booleans represent true or false values. They are commonly used in conditions and control structures.

extends Node


func _ready() -> void:
    var is_game_paused = false
    prints("Is game paused?", is_game_paused)  # Output: Is game paused? false

    var is_game_over: bool = true
    prints("Is game over?", is_game_over)  # Output: Is game over? true

In our _ready function, we've used -> void to indicate it doesn't return a value. GDScript is dynamically typed, but it allows for optional type specifications (e.g., bool, int, String). Employing these type hints can significantly improve code readability, catch errors early, and potentially boost performance.

Integers

Integers represent whole numbers. They are used for counting, indexing, and other operations requiring whole number values.

extends Node


func _ready() -> void:
    var score = 100
    prints("Score:", score)  # Output: Score: 100

    var lives_left: int = 3
    prints("Lives left:", lives_left)  # Output: Lives left: 3
Floats

Floats represent decimal numbers. They are used for precise calculations, measurements, and other operations requiring fractional values.

extends Node


func _ready() -> void:
    var pi = 3.14
    prints("π:", pi)  # Output: π: 3.14

    var gravity: float = 9.8
    prints("Gravity:", gravity)  # Output: Gravity: 9.8
Strings

Strings represent sequences of characters. They are used for storing and manipulating text.

extends Node


func _ready() -> void:
    var greeting = "Hello, Godot!"
    print(greeting)  # Output: Hello, Godot!

    var player_name: String = "Artin"
    prints(player_name, "has entered the game.")  # Output: Artin has entered the game.

    # String manipulation
    print(greeting.to_upper())  # Output: HELLO, GODOT!
    print(player_name.to_lower())  # Output: artin

    var sentence = "GDScript is awesome"
    var words = sentence.split(" ")
    print(words)  # Output: ["GDScript", "is", "awesome"]

    print(sentence.replace("awesome", "amazing"))  # Output: GDScript is amazing

    var padded_name = player_name.lpad(10, "*")
    print(padded_name)  # Output: *****Artin

    print(greeting.begins_with("Hello"))  # Output: true
    print(greeting.ends_with("Godot!"))  # Output: true

    print(sentence.find("is"))  # Output: 9 (index where "is" starts)
Arrays

Arrays are versatile collections that can hold various data types, such as strings, integers, or even other arrays.

extends Node


func _ready() -> void:
    # Creating an array
    var animals = ["Dog", "Cat", "Elephant"]
    # Alternatively, you can define it as:
    #   var animals: Array = ["Dog", "Cat", "Elephant"]
    # Or for stricter type checking:
    #   var animals: Array[String] = ["Dog", "Cat", "Elephant"]
    print(animals)  # Output: ["Dog", "Cat", "Elephant"]

    # Accessing elements
    print(animals[0])  # Output: Dog

    # Modifying elements
    animals[1] = "Lion"
    print(animals)  # Output: ["Dog", "Lion", "Elephant"]

    # Adding elements
    animals.append("Giraffe")
    print(animals)  # Output: ["Dog", "Lion", "Elephant", "Giraffe"]

    # Removing elements
    animals.remove_at(2)
    print(animals)  # Output: ["Dog", "Lion", "Giraffe"]

    # Array length
    print(animals.size())  # Output: 3

    # Sorting
    animals.sort()
    print(animals)  # Output: ["Dog", "Giraffe", "Lion"]

    # Clearing all elements
    animals.clear()
    print(animals)  # Output: []
Dictionaries

Dictionaries are collections of key-value pairs. They are great for storing related data, such as player information, in an organized way.

extends Node


func _ready() -> void:
    # Creating a dictionary
    var player: Dictionary = {
        "name": "Artin", "health": 100, "skills": ["jump", "run", "attack"]
    }
    print(player)  # Output: {"name": "Artin", "health": 100, "skills": ["jump", "run", "attack"]}

    # Accessing values
    print(player["name"])  # Output: Artin

    # Modifying values
    player["health"] = 95
    print(player)  # Output: {"name": "Artin", "health": 95, "skills": ["jump", "run", "attack"]}

    # Adding new key-value pairs
    player["level"] = 5
    print(player)  # Output: {"name": "Artin", "health": 95, "skills": ["jump", "run", "attack"], "level": 5}

    # Removing key-value pairs
    player.erase("level")
    print(player)  # Output: {"name": "Artin", "health": 95, "skills": ["jump", "run", "attack"]}

    # Checking for keys
    print(player.has("name"))  # Output: true

    # Getting all keys or values
    print(player.keys())  # Output: ["name", "health", "skills"]
    print(player.values())  # Output: ["Artin", 95, ["jump", "run", "attack"]]
Operators and Expressions
extends Node


func _ready() -> void:
    var a: int = 10
    var b: int = 20

    # Arithmetic
    print(a + b)  # Output: 30
    print(a - b)  # Output: -10
    print(a * b)  # Output: 200
    print(b / a)  # Output: 2
    print(b % a)  # Output: 0

    # Comparison
    print(a == b)  # Output: false
    print(a != b)  # Output: true
    print(a > b)  # Output: false
    print(a < b)  # Output: true

    # Logical
    var is_ready: bool = true
    var is_paused: bool = false
    print(is_ready and is_paused)  # Output: false
    print(is_ready or is_paused)  # Output: true
    print(not is_ready)  # Output: false

    # Assignment
    var c: int = a + b
    c += 10
    print(c)  # Output: 40
Control Structures
If Statements
extends Node


func _ready() -> void:
    var player_health: int = 75

    if player_health >= 75:
        print("Player health is good")  # This will be printed
    elif player_health >= 30:
        print("Player health is low")
    else:
        print("Player health is critical")

    # Ternary operator (inline if-else)
    var status: String = "Healthy" if player_health >= 50 else "Injured"
    prints("Player status:", status)  # Output: Player status: Healthy
For Loops
extends Node


func _ready() -> void:
    var animals: Array[String] = ["Dog", "Cat", "Elephant"]

    # Iterating over an array
    for animal in animals:
        print(animal)
    # Output:
    # Dog
    # Cat
    # Elephant

    # Using a range
    for i in range(5, 15, 3):
        print(i)
    # Output:
    # 5
    # 8
    # 11
    # 14

    # Iterating over a dictionary
    var player: Dictionary = {"name": "Artin", "health": 100, "level": 5}

    for key in player.keys():
        prints(key, "->", player[key])
    # Output:
    # name -> Artin
    # health -> 100
    # level -> 5
While Loops
extends Node


func _ready() -> void:
    var counter: int = 0

    while counter < 3:
        print(counter)
        counter += 1
    # Output:
    # 0
    # 1
    # 2
Match Statements
extends Node


func _ready() -> void:
    var action: String = "run"

    match action:
        "run":
            print("The player is running!")  # This will be printed
        "jump":
            print("The player is jumping!")
        "attack":
            print("The player is attacking!")
        _:
            print("Unknown action...")

    var fruit: String = "apple"

    match fruit:
        "apple", "banana", "cherry":
            print("This is a common fruit.")  # This will be printed
        "dragonfruit", "durian", "jackfruit":
            print("This is an exotic fruit.")
        _:
            print("Unknown fruit.")
Functions and Built-in Methods

Functions are reusable blocks of code that perform specific tasks. They help organize your code and make it modular. In GDScript, you can define functions using the func keyword.

extends Node

var player_health: int = 100
var max_health: int = 100


func _ready() -> void:
    prints("Game started. Player health:", player_health)

    take_damage(20)
    heal(15)
    take_damage(50)
    heal(100)

    prints("Final health:", player_health)
    prints("Is player alive?", is_alive())


func take_damage(amount: int) -> void:
    player_health = max(0, player_health - amount)
    prints("Player took", amount, "damage. Health:", player_health)


func heal(amount: int) -> void:
    player_health = min(max_health, player_health + amount)
    prints("Player healed", amount, "points. Health:", player_health)


func is_alive() -> bool:
    return player_health > 0

# Output:
# Game started. Player health: 100
# Player took 20 damage. Health: 80
# Player healed 15 points. Health: 95
# Player took 50 damage. Health: 45
# Player healed 100 points. Health: 100
# Final health: 100
# Is player alive? true

GDScript offers a variety of built-in methods to handle common tasks.

Function Description Common Usage
_ready() Called when the node and its children have entered the scene tree. Initialize variables, set up connections, start timers.
_init() Constructor, called when the object is created. Initialize instance variables, rarely used as _ready() is often preferred.
_process(delta) Called every frame, delta is the elapsed time since the previous frame. Update non-physics related game logic, animations, UI.
_physics_process(delta) Called every physics frame (typically 60 times per second). Update physics-related game logic, character movement.
_input(event) Called when an input event occurs. Handle input events that should always be processed.
_unhandled_input(event) Called when an input event is not handled by _input() or any GUI element. Handle input events that weren't handled elsewhere.
_enter_tree() Called when the node enters the scene tree. Set up node in the scene tree, initialize scene-tree dependent variables.
_exit_tree() Called when the node exits the scene tree. Clean up, disconnect signals when node is removed from scene tree.

Advanced GDScript Syntax

Classes and Inheritance

In GDScript, each script defines a class that can inherit from a built-in node or another class. This robust feature enables the development of complex and reusable structures within your game.

Let's start with our base Animal class in the animal.gd file:

class_name Animal
extends Node

var animal_name: String
var animal_age: int


func _init(animal_name_: String, animal_age_: int) -> void:
    animal_name = animal_name_
    animal_age = animal_age_


func speak() -> void:
    prints(animal_name, "makes a noise.")

Next, we'll create a Dog class that inherits from Animal in the dog.gd file:

class_name Dog
extends Animal


func _init(animal_name_: String, animal_age_: int) -> void:
    # Call the parent class (Animal) constructor with the given parameters
    super(animal_name_, animal_age_)


func speak() -> void:
    prints(animal_name, "barks.")

To validate our class implementations without setting up a full scene, let's create a simple test script:

@tool
extends EditorScript


func _run() -> void:
    var dog: Dog = Dog.new("Lola", 7)
    dog.speak()  # Output: Lola barks.
Signals and Connections

Signals allow nodes to communicate with each other in Godot. They are essential for creating interactive and responsive game elements.

Simple illustration of signals

extends Node

signal player_hit(damage: int)
signal enemy_defeated(score: int)

var player_health: int = 100
var score: int = 0


func _ready() -> void:
    # Connect signals
    player_hit.connect(_on_player_hit)
    enemy_defeated.connect(_on_enemy_defeated)

    # Simulate game events
    print("Game started! Player health: %d. Score: %d" % [player_health, score])
    player_hit.emit(20)  # Or `emit_signal("player_hit", 20)`
    enemy_defeated.emit(50)
    player_hit.emit(30)
    enemy_defeated.emit(100)


func _on_player_hit(damage: int) -> void:
    player_health = max(0, player_health - damage)
    print("Player took %d damage. Health: %d" % [damage, player_health])

    if player_health == 0:
        print("GAME OVER!")


func _on_enemy_defeated(points: int) -> void:
    score += points
    print("Enemy defeated! Score increased by %d. New score: %d" % [points, score])

# Output:
# Game started! Player health: 100. Score: 0
# Player took 20 damage. Health: 80
# Enemy defeated! Score increased by 50. New score: 50
# Player took 30 damage. Health: 50
# Enemy defeated! Score increased by 100. New score: 150
Coding Style

GDScript's style guide is a good starting point, but personal preferences vary. While I may not fully agree with it, consistency is crucial. To ensure clean, readable code, use gdformat to automatically format your code according to predefined rules.

You can install it via pip:

pip install "gdtoolkit==4.*"

Then, run it on your GDScript files:

gdformat path/to/your/script.gd

Before formatting:

extends Node

var secret_number
var attempts=       0
func _ready():
        randomize()
        secret_number=randi()%100+1
        print("Welcome to Guess the Number!")

func _process(delta)    :
  if Input.is_action_just_pressed( "ui_accept"):
        var guess=int($GuessInput.text)
        attempts+=1
        if guess==secret_number:
            print("Congratulations! You guessed the number in "+str(attempts)+" attempts.")
            get_tree().quit()
        elif guess<secret_number:print("Too low! Try again.")
        else:print("Too high! Try again.")
        $GuessInput.text=""

After formatting:

extends Node

var secret_number
var attempts = 0


func _ready():
    randomize()
    secret_number = randi() % 100 + 1
    print("Welcome to Guess the Number!")


func _process(delta):
    if Input.is_action_just_pressed("ui_accept"):
        var guess = int($GuessInput.text)
        attempts += 1
        if guess == secret_number:
            print(
                (
                    "Congratulations! You guessed the number in "
                    + str(attempts)
                    + " attempts."
                )
            )
            get_tree().quit()
        elif guess < secret_number:
            print("Too low! Try again.")
        else:
            print("Too high! Try again.")
        $GuessInput.text = ""

If you also install the "GDScript Toolkit," it will automatically install gdlint, which is a tool that checks your GDScript code for possible problems and style mistakes.

Conclusion

GDScript provides a strong but user-friendly base for game development in Godot, making it easy for developers to craft engaging experiences without much hassle. ...I hope this post makes it easier for you to work with the Godot game engine!

Useful links:


Comments on this post

Artin sheikhartin@gmail.com ADMIN Feb. 3, 2025, 5:59 a.m.
Check out my first game project that may help you get inspired and understand how simple things work in Godot: https://github.com/sheikhartin/cute-pong

Leave a comment

Comments can only be deleted by the author of the post...