1. Einführung — Was sich in Godot 4 geändert hat
Godot 4 hat die Funktionsweise von Signalen komplett überarbeitet. Wenn du von Godot 3 kommst: Die alte string-basierte Syntax object.connect("signal_name", target, "method_name") gibt es nicht mehr. An ihre Stelle tritt eine Callable-basierte API, die typsicher und refactoring-freundlich ist und Fehler zur Compile-Zeit statt zur Laufzeit abfängt.
Dieser Leitfaden deckt alles ab, was du über Signale in Godot 4.4+ wissen musst: Deklarieren, Verbinden, Trennen, Auslösen, Awaiten und die nützlichsten Patterns aus der Praxis.
In Godot 4 sind Signale First-Class-Objekte. Du greifst als Eigenschaften auf sie zu (z. B. button.pressed) und rufst Methoden auf ihnen auf (.connect(), .emit(), .disconnect()). Keine string-basierte API mehr.
2. Eigene Signale deklarieren
Deklariere Signale am Anfang deines Skripts mit dem Schlüsselwort signal. Optional kannst du Parameternamen und -typen angeben, für die Dokumentation und die Autovervollständigung im Editor.
# Simple signal with no parameters
signal game_over
# Signal with typed parameters
signal health_changed(new_health: int)
# Signal with multiple parameters
signal item_picked_up(item_name: String, quantity: int)
# Signal with no type hints (works, but typed is recommended)
signal something_happened(data)
Füge Signalparametern immer Typangaben hinzu. Das aktiviert die Autovervollständigung im Editor und macht deinen Code selbstdokumentierend. Signale mit Typangaben zeigen zudem Parameterinfos im Signals-Tab des Node-Docks.
3. Signale verbinden (die neue Methode)
Die größte Änderung in Godot 4 ist die Art, wie du Signale verbindest. Statt Strings zu übergeben, verwendest du das Signal als Eigenschaft und rufst .connect() mit einem Callable (einer Referenz auf eine Funktion) auf.
Grundlegende Verbindung
# Godot 3 (OLD — no longer works in Godot 4)
# button.connect("pressed", self, "_on_button_pressed")
# Godot 4 — callable-based connection
button.pressed.connect(_on_button_pressed)
func _on_button_pressed() -> void:
print("Button was pressed!")
Verbindung mit zusätzlichen Argumenten via bind()
Verwende .bind(), um zusätzliche Daten zusammen mit dem Signal zu übergeben. Das ist praktisch, wenn du mehrere Signale mit derselben Methode verbindest.
# Pass extra data via bind()
buy_button.pressed.connect(_on_item_button.bind("sword"))
sell_button.pressed.connect(_on_item_button.bind("shield"))
func _on_item_button(item_id: String) -> void:
print("Selected item: ", item_id)
Lambda- (Inline-)Verbindungen
Für kurze Handler kannst du direkt eine Lambda-Funktion verwenden. So bleibt einfache Logik nah am Verbindungspunkt.
# Lambda — great for one-liners
button.pressed.connect(func(): print("Button pressed!"))
# Lambda with parameters
health_changed.connect(func(hp: int): health_label.text = str(hp))
# Multi-line lambda
enemy.died.connect(func():
score += 100
score_label.text = "Score: %d" % score
print("Enemy defeated!")
)
Lambda-Verbindungen lassen sich nicht ohne Weiteres trennen, weil du keine Referenz auf die anonyme Funktion hast. Wenn du später trennen musst, speichere das Callable in einer Variablen oder verwende stattdessen eine benannte Methode.
Im Editor verbinden (Node-Dock)
Du kannst Signale weiterhin über die Godot-Editor-UI verbinden. Wähle einen Node aus, öffne das Node-Dock > Signals-Tab, doppelklicke auf ein Signal und wähle Ziel-Node und -Methode. Der Editor generiert automatisch eine Methode wie _on_button_pressed() in deinem Skript. Das ist identisch mit dem Aufruf von .connect() im Code — es spart nur Tipparbeit.
4. Signale trennen
Trenne ein Signal, wenn du es nicht mehr empfangen möchtest. Das ist wichtig, um Fehler zu vermeiden, wenn Nodes freigegeben werden oder der Spielzustand wechselt.
# Disconnect a signal
button.pressed.disconnect(_on_button_pressed)
# Always check before disconnecting to avoid errors
if button.pressed.is_connected(_on_button_pressed):
button.pressed.disconnect(_on_button_pressed)
Trenne szenenübergreifende Signalverbindungen (z. B. zu einem Autoload) immer in _exit_tree(), um hängende Referenzen zu verhindern, wenn der Node freigegeben wird:
func _ready() -> void:
EventBus.player_died.connect(_on_player_died)
func _exit_tree() -> void:
if EventBus.player_died.is_connected(_on_player_died):
EventBus.player_died.disconnect(_on_player_died)
5. Signale auslösen
In Godot 4 verwendest du .emit() statt des alten emit_signal(). Das Signal ist ein Objekt, also rufst du die Methode direkt darauf auf.
signal health_changed(new_health: int)
signal died
var health: int = 100
func take_damage(amount: int) -> void:
health -= amount
health_changed.emit(health) # Emit with argument
if health <= 0:
died.emit() # Emit with no arguments
Das alte emit_signal("signal_name") funktioniert in Godot 4 technisch noch, ist aber veraltet. Bevorzuge für neuen Code immer signal_name.emit(). Die string-basierte Variante könnte in einem zukünftigen Godot-Release entfernt werden.
6. Auf Signale warten (await)
Godot 4 hat yield() durch await ersetzt. Damit kannst du eine Funktion pausieren, bis ein Signal auslöst — perfekt für Cutscenes, Tutorials, sequenzielle Animationen und zeitgesteuerte Ereignisse.
func play_cutscene() -> void:
# Wait for a timer
await get_tree().create_timer(2.0).timeout
# Wait for an animation to finish
animation_player.play("intro")
await animation_player.animation_finished
# Wait for player input (custom signal)
dialogue_label.text = "Press any key to continue..."
await player_pressed_continue
# Continue execution after the signal fires
print("Cutscene complete!")
Werte aus awaiteten Signalen erhalten
Wenn das Signal Argumente auslöst, gibt await sie zurück. Bei einem einzelnen Argument erhältst du den Wert direkt. Bei mehreren Argumenten erhältst du ein Array.
# Single parameter — returns the value directly
var final_health: int = await health_changed
print("Health is now: ", final_health)
# Multiple parameters — returns an array
var result = await item_picked_up
var item_name: String = result[0]
var quantity: int = result[1]
7. Verbindungs-Flags für Signale
Godot bietet Verbindungs-Flags als zweites Argument von .connect(), mit denen du das Verhalten der Verbindung anpassen kannst.
CONNECT_ONE_SHOT
Die Verbindung wird automatisch entfernt, nachdem das Signal einmal ausgelöst hat. Perfekt für einmalige Ereignisse wie Todesanimationen oder das Freischalten von Erfolgen.
# Auto-disconnects after firing once
enemy.died.connect(_on_first_kill, CONNECT_ONE_SHOT)
func _on_first_kill() -> void:
unlock_achievement("first_blood")
CONNECT_DEFERRED
Die verbundene Methode wird am Ende des aktuellen Frames (während der Idle-Zeit) aufgerufen statt sofort. Nützlich, wenn der Signal-Handler den Szenenbaum verändert, was während der Physik- oder Signalverarbeitung nicht sicher ist.
# Called at end of frame — safe for scene tree changes
button.pressed.connect(_on_restart, CONNECT_DEFERRED)
func _on_restart() -> void:
get_tree().reload_current_scene()
Flags kombinieren
# One-shot AND deferred
trigger.body_entered.connect(_on_trigger, CONNECT_ONE_SHOT | CONNECT_DEFERRED)
8. Häufige Patterns
EventBus (globaler Signal-Hub)
Das EventBus-Pattern verwendet ein Autoload-Singleton, um Nodes zu entkoppeln. Jeder Node kann globale Ereignisse auslösen oder darauf hören, ohne eine direkte Referenz auf einen anderen Node zu benötigen. Das ist eines der mächtigsten Patterns in Godot.
extends Node
# Define all global signals in one place
signal player_died
signal score_changed(new_score: int)
signal level_completed(level_id: int)
signal item_collected(item_name: String)
signal settings_changed
func die() -> void:
# Any script can emit global signals
EventBus.player_died.emit()
func _ready() -> void:
# Any script can listen to global signals
EventBus.score_changed.connect(_on_score_changed)
EventBus.player_died.connect(_on_player_died)
func _on_score_changed(new_score: int) -> void:
score_label.text = "Score: %d" % new_score
func _on_player_died() -> void:
game_over_screen.show()
Gehe zu Projekt > Projekteinstellungen > Autoload, füge dein Skript event_bus.gd hinzu und benenne es EventBus. Es wird automatisch zu einem global zugänglichen Singleton.
Signal-Relay (Eltern-Kind-Kommunikation)
Ein Eltern-Node hört auf die Signale seiner Kinder und leitet Informationen weiter oder aggregiert sie. Die Kinder müssen nie voneinander wissen.
signal inventory_updated
func _ready() -> void:
# Connect to all slot children
for slot in get_children():
if slot.has_signal("item_changed"):
slot.item_changed.connect(_on_slot_changed)
func _on_slot_changed() -> void:
inventory_updated.emit()
Signal + Await für sequenziellen Spielablauf
func run_level() -> void:
spawn_enemies()
await EventBus.all_enemies_defeated
show_treasure_chest()
await EventBus.chest_opened
play_exit_animation()
await get_tree().create_timer(1.5).timeout
load_next_level()
Dynamische Verbindungen für gespawnte Nodes
func spawn_enemy(pos: Vector2) -> void:
var enemy = enemy_scene.instantiate()
enemy.position = pos
# Connect signals before adding to scene tree
enemy.died.connect(_on_enemy_died.bind(enemy))
enemy.health_changed.connect(_on_enemy_health_changed)
add_child(enemy)
func _on_enemy_died(enemy: Node) -> void:
enemies_alive -= 1
enemy.queue_free()
9. Migrations-Spickzettel (Godot 3 → 4)
Setze ein Lesezeichen auf diese Tabelle als schnelle Referenz beim Portieren deines Godot-3-Projekts.
| Operation | Godot 3 | Godot 4 |
|---|---|---|
| Verbinden | connect("sig", obj, "method") |
sig.connect(method) |
| Trennen | disconnect("sig", obj, "method") |
sig.disconnect(method) |
| Auslösen | emit_signal("sig", args) |
sig.emit(args) |
| Verbindung prüfen | is_connected("sig", obj, "method") |
sig.is_connected(method) |
| Auf Signal warten | yield(obj, "sig") |
await obj.sig |
| Zusätzliche Argumente binden | connect("sig", obj, "method", [data]) |
sig.connect(method.bind(data)) |
| One-Shot | connect("sig", obj, "method", [], CONNECT_ONESHOT) |
sig.connect(method, CONNECT_ONE_SHOT) |
10. Häufige Fehler
1. String-basiertes connect (Godot-3-Stil) verwenden
# WRONG — Godot 3 syntax, will not compile
button.connect("pressed", self, "_on_button_pressed")
# CORRECT — Godot 4 syntax
button.pressed.connect(_on_button_pressed)
2. emit_signal() statt .emit() verwenden
Zwar funktioniert emit_signal() noch, aber es umgeht Prüfungen zur Compile-Zeit und ist offiziell veraltet. Verwende .emit().
# AVOID — deprecated, no compile-time checks
emit_signal("health_changed", health)
# PREFER — type-safe, catches typos at compile time
health_changed.emit(health)
3. Verbindung zu einem freigegebenen Node
Wenn du ein Signal mit einer Methode auf einem Node verbindest und dieser Node per queue_free() freigegeben wird, stürzt die nächste Signalauslösung ab. Lösungen:
- In
_exit_tree()trennen - Für einmalige Ereignisse
CONNECT_ONE_SHOTverwenden - Vor dem Auslösen mit
is_instance_valid(target)prüfen
# Safe emission pattern
for connection in my_signal.get_connections():
if is_instance_valid(connection["callable"].get_object()):
pass # Connection is still valid
my_signal.emit() # Godot handles invalid connections gracefully in 4.x
4. Vergessen, szenenübergreifende Signale zu trennen
Verbindungen zu Autoload-Signalen bleiben über Szenenwechsel hinweg bestehen. Wenn du in _ready() verbindest, aber nie trennst, bekommst du Fehler oder unerwartetes Verhalten, wenn die Szene neu geladen wird.
# WRONG — never disconnects, leaks across scene changes
func _ready() -> void:
EventBus.score_changed.connect(_on_score_changed)
# CORRECT — clean disconnect
func _ready() -> void:
EventBus.score_changed.connect(_on_score_changed)
func _exit_tree() -> void:
EventBus.score_changed.disconnect(_on_score_changed)
5. Dasselbe Signal zweimal verbinden
Wenn _ready() mehrmals aufgerufen wird (z. B. beim Umhängen eines Nodes), verbindest du womöglich versehentlich dieselbe Methode zweimal. Der Handler feuert dann pro Auslösung zweimal.
# Guard against double-connection
func _ready() -> void:
if not EventBus.score_changed.is_connected(_on_score_changed):
EventBus.score_changed.connect(_on_score_changed)
Soll KI deine Signal-Architektur verwalten?
Godot MCP Pro verbindet KI-Assistenten wie Claude direkt mit deinem Godot-Editor. Es kann Signalflüsse in deinem gesamten Projekt verbinden, trennen, auditieren und visualisieren — automatisch.
analyze_signal_flow
find_signal_connections
connect_signal
disconnect_signal
get_signals