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.

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.

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: