In Godot, a signal is a notification sent by a node.
Something happened. Whoever cares about it can respond.
Examples:
- Button: I was pressed.
- Coin: I was collected by the player.
- Player: My health changed.
- Enemy: I died.
- Timer: Time is up.
Other nodes can connect to these signals and run a function when the signal is emitted. The sender does not need to directly find the receiver, which reduces coupling between nodes.
The three core steps
A complete signal flow usually has three steps:
- Declare the signal.
- Connect the signal.
- Emit the signal.
Think of it like a doorbell:
| Concept | Analogy |
|---|---|
signal |
Install the doorbell |
connect |
Wire the doorbell to the indoor chime |
emit |
Press the doorbell |
| Receiver function | Someone hears it and opens the door |
The key memory aid is:
|
|
Two kinds of signals in Godot
Godot signals commonly fall into two types: built-in signals and custom signals.
Built-in signals
Built-in signals are signals already provided by Godot nodes.
Common examples:
Button.pressedTimer.timeoutArea2D.body_enteredArea2D.body_exited
If a coin node extends Area2D, it already has the body_entered signal. When a CharacterBody2D enters the coin’s collision area, Godot emits that signal automatically.
Custom signals
Custom signals are declared by us.
For example, a coin being collected:
|
|
This collected signal is not built into Godot. It is a notification type we define ourselves.
Two signal layers in the coin example
A simple coin script can look like this:
|
|
This script actually uses two signal layers.
First layer: body_entered
This line:
|
|
means:
When a physics object enters Coin’s
Area2D, call_on_body_entered().
The flow is:
|
|
The body parameter is the node that entered the coin area. If Player enters the area, then:
|
|
Second layer: collected
This line:
|
|
declares a signal, meaning Coin can send a notification called collected.
Then:
|
|
means:
This coin has been collected.
But remember: emitting a signal does not guarantee that anyone receives it.
If no node is connected to collected, the signal is still emitted, but nothing else happens. The coin disappears because of this line:
|
|
It does not disappear automatically because of collected.emit().
Full communication flow
When the player touches the coin, the full flow is:
|
|
There are two senders here:
|
|
body_entered tells Coin: “Something entered your area.”
collected tells Main: “This coin was collected.”
How Main receives the coin signal
Assume this scene structure:
|
|
You can connect the signal in the editor or in code.
Method 1: connect in the Godot editor
Open main.tscn.
First, select this node in the scene tree:
|
|
Second, switch the right panel from Inspector to:
|
|
In the Godot UI this panel is:
|
|
Third, find the custom signal:
|
|
Double-click it.
Fourth, choose the receiver node:
|
|
Click Connect. Godot will generate a function similar to this in Main’s script:
|
|
Change it to:
|
|
When you run the game and collect the coin, it prints:
|
|
Method 2: connect signals in code
You can also connect it directly in main.gd:
|
|
The key line is:
|
|
It means:
|
|
In Godot 4, the recommended style is to call connect() through the Signal object:
|
|
Do not blindly copy old Godot 3 tutorials that use older connection syntax.
Let signals carry data
Signals can do more than say “something happened.” They can carry data too.
For example, different coins can have different values:
|
|
A normal coin can use:
|
|
A large coin can use:
|
|
Main must receive the parameter too:
|
|
The communication becomes:
|
|
Why Coin should not directly modify the UI
This is not recommended:
|
|
Because Coin now depends on Main’s exact node structure:
- Coin must know where
ScoreLabelis. - Coin must know how the score is stored.
- Coin must know how the UI is updated.
If you move the UI node, Coin’s path may break.
A better structure is:
|
|
In other words:
|
|
This is how signals reduce coupling between nodes.
Three concepts that are easy to mix up
signal
Declare a signal:
|
|
This creates a notification type.
connect()
Specify which function should run after the signal is emitted:
|
|
emit()
Actually emit the signal:
|
|
Memory aid:
|
|
Recommended coin code
coin.gd:
|
|
main.gd:
|
|
The key idea is:
Signals notify that something happened. They do not decide everything every other node should do next.
Once you understand this, buttons, coins, enemies, timers, health changes, and UI updates in Godot become much easier to organize.