Overview
As you develop in Godot, nodes often need to interact. "When the player touches a coin, the coin disappears and the UI score increases." A common beginner mistake is directly referencing coin or UI nodes from the player's script (using get_node() or $). However, this approach creates strong dependencies between nodes and makes future changes extremely difficult.
The fundamental mechanism in Godot for solving this problem and keeping nodes "loosely coupled" is Signals.
What are Signals? A "Hey, Something Happened!" Notification
Signals are messages (notifications) that one node sends to inform other nodes that "a specific event occurred." The signal sender (Emitter) doesn't need to know who receives the message—it just sends the notification.
On the other hand, nodes that want to receive messages (Receivers) subscribe ("Connect") to specific signals from specific nodes. When a signal is emitted, their designated method (Callback) is automatically called.
This mechanism allows Emitters and Receivers to communicate without directly knowing each other. This is a fundamental software design principle known as "separation of concerns."
Using Signals: Connecting via the Editor
The easiest and most visual approach is connecting signals through the Godot editor.
-
Select the signal-emitting node: In the scene tree, select a node with signals (e.g., a
Buttonnode) -
Open the "Node" tab: Open the "Node" tab next to Inspector, then select the "Signals" tab. You'll see a list of signals that node can emit (e.g.,
pressed()) -
Connect the signal: Double-click the signal you want to connect (e.g.,
pressed()). The "Connect a Signal" dialog opens -
Select receiver and method: In the dialog, select the node to receive the signal (Receiver) and determine the method name to be called (usually the auto-generated name is fine). Click "Connect" to finish
This automatically generates a method with the specified name in the Receiver's script. Just write the processing you want when the signal is received inside that method.
# Auto-generated in Receiver's script
func _on_button_pressed():
print("Button was pressed!")
# Write your processing here
Defining and Emitting Custom Signals
Beyond built-in signals like Button's pressed, defining your own custom signals is extremely important.
1. Define the signal: Declare custom signals at the beginning of your script using the signal keyword. Arguments can be passed as well.
# Player.gd
extends CharacterBody2D
# Define a custom signal named "health_changed"
# Allow passing new HP value as argument
signal health_changed(new_health)
var health = 100
func take_damage(amount):
health -= amount
# Emit the signal
health_changed.emit(health)
if health <= 0:
die()
2. Emit the signal: Call signal_name.emit() when you want to emit the signal. If you defined arguments, pass those values to emit().
3. Connect: This custom signal can be connected to other nodes (e.g., UI HP bar) through the editor's "Node" tab, just like built-in signals. The HP bar script just needs to receive the health_changed signal and update its display using the passed new_health value. It doesn't need to know the details of how the player took damage.
Summary
Signals are the cleanest and recommended way to communicate between nodes in Godot.
- Emitter side: Just shout "something happened!" without caring who's listening
- Receiver side: Register for notifications you want to hear, then do your job when called
By designing components with this loose coupling in mind, you increase scene reusability, reduce bugs, and build projects that are resilient to changes. When you find yourself writing code that directly references parents or siblings with get_node(), pause and ask yourself "Could this be done with signals?" Developing this habit is the first step to becoming a Godot master.