Signals
Basic Signal Declaration & Emission
# ============================================
# DECLARING SIGNALS
# ============================================
extends Node
# Simple signal
signal health_changed
# Signal with parameters
signal player_died(player_name: String)
signal damage_taken(amount: int, damage_type: String)
func take_damage(amount: int):
health -= amount
health_changed.emit()
if health <= 0:
player_died.emit(player_name)
Connecting Signals
# ============================================
# METHOD 1: Code Connection
# ============================================
extends Node
func _ready():
# Connect to another node's signal
$Player.health_changed.connect(_on_player_health_changed)
$Player.player_died.connect(_on_player_died)
func _on_player_health_changed():
print("Player health changed!")
func _on_player_died(player_name: String):
print(player_name + " has died!")
# ============================================
# METHOD 2: Editor Connection (Inspector)
# ============================================
# 1. Select the node with the signal in Scene tree
# 2. Go to Node tab (next to Inspector)
# 3. Double-click signal → pick receiver node → select/create method
# 4. Godot auto-generates method with correct signature
# ============================================
# METHOD 3: Lambda/Anonymous Functions
# ============================================
func _ready():
$Button.pressed.connect(func(): print("Button pressed!"))
# With parameters
$Player.damage_taken.connect(func(amount, type):
print("Took " + str(amount) + " " + type + " damage")
)
Advanced Signal Patterns
# ============================================
# DISCONNECTING SIGNALS
# ============================================
func _ready():
$Player.health_changed.connect(_on_health_changed)
func cleanup():
if $Player.health_changed.is_connected(_on_health_changed):
$Player.health_changed.disconnect(_on_health_changed)
# ============================================
# ONE-SHOT SIGNALS (Connect once, auto-disconnect)
# ============================================
func _ready():
# CONNECT_ONE_SHOT flag
$Timer.timeout.connect(_on_timer_timeout, CONNECT_ONE_SHOT)
func _on_timer_timeout():
print("This only fires once!")
# ============================================
# SIGNAL CHAINING
# ============================================
extends Node
signal game_started
signal wave_started(wave_number: int)
func _ready():
game_started.connect(_on_game_started)
func _on_game_started():
# One signal triggers another
wave_started.emit(1)
Global Signal Bus Pattern
# ============================================
# AUTOLOAD: SignalBus.gd
# ============================================
extends Node
# UI signals
signal score_changed(new_score: int)
signal show_notification(message: String)
# Game state signals
signal level_completed(level_id: int)
signal enemy_spawned(enemy: Node)
# This is accessible from anywhere as SignalBus.signal_name
# ============================================
# USING SIGNAL BUS - Any Script
# ============================================
extends Node
func _ready():
# Connect to global signals
SignalBus.score_changed.connect(_on_score_changed)
SignalBus.level_completed.connect(_on_level_completed)
func collect_coin():
# Emit global signal from anywhere
SignalBus.score_changed.emit(current_score)
SignalBus.show_notification.emit("Coin collected!")
func _on_score_changed(new_score: int):
print("New score: " + str(new_score))
Built-in Node Signals
# ============================================
# COMMON BUILT-IN SIGNALS
# ============================================
# Node signals
ready # When node enters scene tree
tree_entered
tree_exiting
# Button signals
pressed
toggled(toggled_on: bool)
# Timer signals
timeout
# AnimationPlayer signals
animation_finished(anim_name: String)
# Area2D/Area3D signals
body_entered(body: Node2D)
body_exited(body: Node2D)
area_entered(area: Area2D)
# Example usage
func _ready():
$Button.pressed.connect(_on_button_pressed)
$Timer.timeout.connect(_on_timer_timeout)
$AnimationPlayer.animation_finished.connect(_on_animation_finished)
Best Practices
# ============================================
# NAMING CONVENTIONS
# ============================================
# Signal names: past tense or state change
signal died
signal health_changed
signal item_collected
# Handler methods: _on_source_signal_name
func _on_player_died():
pass
func _on_enemy_health_changed():
pass
# ============================================
# CLEANUP
# ============================================
extends Node
var connected_signals = []
func _ready():
$Player.died.connect(_on_player_died)
func _exit_tree():
# Disconnect all signals when node is removed
if $Player and $Player.died.is_connected(_on_player_died):
$Player.died.disconnect(_on_player_died)
When to use what:
- Custom Signals - Communication between specific game objects
- Signal Bus - Global events (UI updates, game state changes)
- Built-in Signals - Responding to engine events (collisions, animations, input)
- Code Connection - Dynamic connections, flexibility needed
- Editor Connection - Simple static connections, easier debugging