Signals

Signals let a node announce that something happened without knowing who listens.

17.1 What signals are

Signals let a node announce that something happened without knowing who listens. Listeners connect callables to them. This decouples your code ("signal up, call down").

17.2 Declaring and emitting

class_name Player
extends CharacterBody2D

signal died
signal health_changed(old_value: int, new_value: int)
signal item_collected(item_name: String, amount: int)

var health := 100

func take_damage(amount: int) -> void:
	var old := health
	health = max(health - amount, 0)
	health_changed.emit(old, health)
	if health == 0:
		died.emit()

17.3 Connecting in code

Bound arguments (bind) are passed after the emitted arguments.

signal_name.disconnect(callable), signal_name.is_connected(callable).

When either object is freed, its connections are removed automatically (lambdas capturing freed objects can error — prefer methods).

func _ready() -> void:
	var player: Player = $Player
	player.health_changed.connect(_on_player_health_changed)
	player.died.connect(_on_player_died, CONNECT_ONE_SHOT)       # auto-disconnect after first emit
	player.item_collected.connect(func(item, n): print(item, " x", n))
	$Timer.timeout.connect(spawn_enemy.bind("slime"))           # extra bound argument
	$Button.pressed.connect(_on_button_pressed, CONNECT_DEFERRED) # call at idle time

func _on_player_health_changed(old_value: int, new_value: int) -> void:
	$HUD/HealthBar.value = new_value

func _on_player_died() -> void:
	get_tree().reload_current_scene()

func spawn_enemy(kind: String) -> void:
	print("spawning ", kind)

17.4 Connecting in the editor

Select the emitting node → Node dock → Signals → double-click a signal → choose the receiving node → the editor creates a method like _on_button_pressed. Editor connections are stored in the .tscn and show a green icon next to the method in the script editor.

17.5 Common built-in signals

Button / BaseButton pressed, toggled(on), button_down, button_up

body_entered(body), body_exited, area_entered(area), area_exited

animation_finished(anim_name), animation_started ready, tree_entered, tree_exiting, tree_exited, renamed screen_entered, screen_exited

text_changed(text), text_submitted(text) process_frame, physics_frame, node_added, tree_changed

finished, step_finished, loop_finished request_completed(result, code, headers, body)

17.6 Signal objects and dynamic signals

var s: Signal = $Button.pressed
s.connect(_on_pressed)
print(s.get_name(), s.get_connections())
emit_signal("died")                       # string-based (legacy style, still works)
connect("died", _on_died)
add_user_signal("custom_event", [{"name": "value", "type": TYPE_INT}])

17.7 Signal bus (event bus) pattern

An autoload with only signals lets distant nodes talk:

Godot 3 note: connect("pressed", self, "_on_pressed", [extra]) and emit_signal("died"). In 4.x signals and callables are first-class.

# res://autoload/events.gd  (registered as autoload "Events")
extends Node

signal coin_collected(value: int)
signal player_died
signal level_completed(level: int)
# coin.gd
func _on_body_entered(_body: Node2D) -> void:
	Events.coin_collected.emit(10)
	queue_free()

# hud.gd
func _ready() -> void:
	Events.coin_collected.connect(func(v): score += v; update_label())