Signals
Signals are Godot's event system, letting nodes communicate without directly knowing about each other, one of the most distinctive and important GDScript features to understand well.
# Health.gd, attached to a Player node
extends Node
signal health_depleted
signal health_changed(new_value)
var health := 100
func take_damage(amount: int):
health -= amount
health_changed.emit(health)
if health <= 0:
health_depleted.emit()
# UI.gd, attached to a completely separate HUD node
func _ready():
var player = get_node("/root/Game/Player")
player.health_changed.connect(_on_health_changed)
player.health_depleted.connect(_on_player_died)
func _on_health_changed(new_value):
print("Health is now: ", new_value)
func _on_player_died():
print("Game over!")
Why signals instead of direct calls?
The Health script has no idea the UI even exists, it just emits when something happens. The UI script connects to those signals separately. This means the Player's health logic and the HUD's display logic are completely decoupled, you can add a second listener (a sound effect script, an achievement tracker) without touching Health.gd at all, and swap out the UI entirely without touching gameplay code.
This pattern (also called "observer" or "pub/sub" in other languages) is exactly why Godot projects tend to stay maintainable as they grow, systems talk about events, not directly to each other.