A signal is a message a node sends when something happens: a button was pressed, the player died, a coin was collected. Other code connects to the signal and runs when it fires. The sender does not need to know who is listening:
Health: 10 -> 6
Health: 6 -> 0
Game over
Three steps:
- Declare the signal with
signal, listing its arguments. - Connect a function to it with
signal_name.connect(function). - Emit it with
signal_name.emit(arguments). Every connected function runs, in the order they were connected.
Why Signals Matter
Signals keep nodes independent. A Player emits died, and the HUD, the music and the level each connect to it. None of them needs a reference to the others, and the Player scene works on its own. The Godot rule of thumb is "call down, signal up": a parent calls methods on its children, and a child sends signals to whoever is above it.
Built-in Signals
Every node type comes with signals. A few you will use early:
| Node | Signal | When |
|---|---|---|
Button | pressed | the button is clicked |
Area2D | body_entered(body) | a physics body enters the area |
Timer | timeout | the time is up |
AnimationPlayer | animation_finished(name) | an animation ends |
any Node | tree_exiting | the node is about to leave the tree |
Connect them in code the same way:
func _ready():
$StartButton.pressed.connect(_on_start_pressed)
$Hitbox.body_entered.connect(_on_hitbox_body_entered)
func _on_hitbox_body_entered(body: Node2D) -> void:
if body.is_in_group("player"):
body.take_damage(1)
Or connect them in the editor: select the node, open the Node dock, double click the signal and pick the script. Godot writes an empty handler such as _on_start_button_pressed() for you.
Connecting a Lambda
For a short reaction, connect a lambda instead of writing a method:
+5 coins
+1 coins
Total: 6
One-Shot Connections, Disconnecting and Checks
first time only
level started
level started
true
0
CONNECT_ONE_SHOT disconnects after the first emit. Connecting the same function twice is an error, so connect once, usually in _ready():
ERROR: Signal 'level_started' is already connected to given callable 'Node(main.gd)::_on_level_started' in that object.
Passing Extra Data with bind()
A signal's arguments are fixed by the emitter. To add your own, bind() them to the handler. This tells several buttons apart with one function:
func _ready():
$Shop/BuySword.pressed.connect(_on_buy_pressed.bind("sword"))
$Shop/BuyShield.pressed.connect(_on_buy_pressed.bind("shield"))
func _on_buy_pressed(item: String) -> void:
print("Buying ", item)
Waiting for a Signal
await pauses a function until a signal fires, which reads like a script of events:
func open_chest() -> void:
$AnimationPlayer.play("open")
await $AnimationPlayer.animation_finished
spawn_loot()
See await for coroutines and timers.
Godot 3 vs Godot 4
| Godot 3 | Godot 4 |
|---|---|
connect("died", self, "_on_died") | died.connect(_on_died) |
emit_signal("died") | died.emit() (the old form still works) |
disconnect("died", self, "_on_died") | died.disconnect(_on_died) |
yield(self, "died") | await died |
Frequently Asked Questions
How do I connect a signal in Godot 4?
Call connect() on the signal with the function to run: $Button.pressed.connect(_on_button_pressed). Pass the method name without parentheses. You can also connect in the editor's Node dock, which writes the handler function for you.
How do I create a custom signal in GDScript?
Declare it at the top of the script: signal died or, with arguments, signal health_changed(old_value: int, new_value: int). Fire it with died.emit() or health_changed.emit(10, 4).
What replaced emit_signal and the string connect in Godot 4?
Godot 4 treats a signal as a value: died.emit() and died.connect(_on_died). The old emit_signal("died") still works, but the Godot 3 form connect("died", self, "_on_died") does not.
What does "Signal is already connected to given callable" mean?
You connected the same function to the same signal twice, often because connect() runs in a function that is called more than once. Connect once in _ready(), or check first with is_connected().