1. Wprowadzenie — Co się zmieniło w Godot 4
Godot 4 całkowicie przebudował sposób działania sygnałów. Jeśli przechodzisz z Godot 3, stara składnia oparta na łańcuchach znaków object.connect("signal_name", target, "method_name") odeszła. W jej miejsce Godot 4 używa API opartego na Callable, które jest bezpieczne typowo, przyjazne refaktoryzacji i wychwytuje błędy na etapie kompilacji, a nie w czasie działania.
Ten poradnik obejmuje wszystko, co musisz wiedzieć o sygnałach w Godot 4.4+: deklarowanie, łączenie, rozłączanie, wysyłanie, oczekiwanie oraz najbardziej przydatne wzorce z praktyki.
W Godot 4 sygnały są obiektami pierwszej klasy. Uzyskujesz do nich dostęp jako do właściwości (np. button.pressed) i wywołujesz na nich metody (.connect(), .emit(), .disconnect()). Koniec z API opartym na łańcuchach znaków.
2. Deklarowanie własnych sygnałów
Deklaruj sygnały na początku swojego skryptu za pomocą słowa kluczowego signal. Opcjonalnie możesz podać nazwy i typy parametrów na potrzeby dokumentacji oraz autouzupełniania w edytorze.
# 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)
Zawsze dodawaj podpowiedzi typów do parametrów sygnałów. Włącza to autouzupełnianie w edytorze i sprawia, że kod sam się dokumentuje. Sygnały z podpowiedziami typów pokazują też informacje o parametrach w zakładce Sygnały w doku Node.
3. Łączenie sygnałów (nowy sposób)
Największą zmianą w Godot 4 jest sposób łączenia sygnałów. Zamiast przekazywać łańcuchy znaków, używasz sygnału jako właściwości i wywołujesz .connect() z obiektem Callable (referencją do funkcji).
Podstawowe połączenie
# 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!")
Połączenie z dodatkowymi argumentami przez bind()
Użyj .bind(), aby przekazać dodatkowe dane razem z sygnałem. Jest to przydatne przy łączeniu wielu sygnałów z tą samą metodą.
# 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)
Połączenia z lambdą (inline)
W przypadku krótkich handlerów możesz użyć funkcji lambda bezpośrednio. Dzięki temu prosta logika pozostaje blisko miejsca połączenia.
# 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!")
)
Połączeń z lambdą nie da się łatwo rozłączyć, ponieważ nie masz referencji do funkcji anonimowej. Jeśli będziesz musiał rozłączyć je później, zapisz obiekt Callable w zmiennej albo użyj nazwanej metody.
Łączenie w edytorze (dok Node)
Sygnały wciąż można łączyć przez interfejs edytora Godot. Zaznacz węzeł, otwórz dok Node > zakładka Sygnały, kliknij dwukrotnie sygnał i wybierz docelowy węzeł oraz metodę. Edytor automatycznie wygeneruje w twoim skrypcie metodę taką jak _on_button_pressed(). Jest to identyczne z wywołaniem .connect() w kodzie — oszczędza tylko pisania.
4. Rozłączanie sygnałów
Rozłącz sygnał, gdy nie chcesz go już otrzymywać. Jest to ważne, aby unikać błędów, gdy węzły są zwalniane lub przy przełączaniu stanów gry.
# 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)
W przypadku połączeń sygnałów między scenami (np. połączenia z Autoload) zawsze rozłączaj je w _exit_tree(), aby zapobiec zawisłym referencjom po zwolnieniu węzła:
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. Wysyłanie sygnałów
W Godot 4 używaj .emit() zamiast starego emit_signal(). Sygnał jest obiektem, więc wywołujesz metodę bezpośrednio na nim.
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
Stare emit_signal("signal_name") technicznie wciąż działa w Godot 4, ale jest przestarzałe. W nowym kodzie zawsze preferuj signal_name.emit(). Wersja oparta na łańcuchach znaków może zostać usunięta w przyszłym wydaniu Godot.
6. Oczekiwanie na sygnały (await)
Godot 4 zastąpił yield() słowem await. Pozwala to wstrzymać funkcję do momentu wysłania sygnału, co świetnie sprawdza się w scenkach przerywnikowych, samouczkach, sekwencyjnych animacjach i zdarzeniach czasowych.
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!")
Pobieranie wartości z oczekiwanych sygnałów
Jeśli sygnał wysyła argumenty, await je zwraca. Dla pojedynczego argumentu otrzymujesz wartość bezpośrednio. Dla wielu argumentów otrzymujesz tablicę.
# 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. Flagi połączeń sygnałów
Godot udostępnia flagi połączeń jako drugi argument metody .connect(), które modyfikują zachowanie połączenia.
CONNECT_ONE_SHOT
Połączenie jest automatycznie usuwane po jednokrotnym wysłaniu sygnału. Idealne do jednorazowych zdarzeń, takich jak animacje śmierci czy odblokowanie osiągnięć.
# 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
Połączona metoda jest wywoływana na końcu bieżącej klatki (w czasie bezczynności) zamiast natychmiast. Przydatne, gdy handler sygnału modyfikuje drzewo sceny, czego nie można bezpiecznie robić podczas przetwarzania fizyki lub sygnałów.
# 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()
Łączenie flag
# One-shot AND deferred
trigger.body_entered.connect(_on_trigger, CONNECT_ONE_SHOT | CONNECT_DEFERRED)
8. Popularne wzorce
EventBus (globalny hub sygnałów)
Wzorzec EventBus wykorzystuje singleton Autoload, aby rozdzielić węzły. Dowolny węzeł może wysyłać lub nasłuchiwać globalnych zdarzeń bez potrzeby bezpośredniej referencji do innego węzła. To jeden z najpotężniejszych wzorców w 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()
Przejdź do Projekt > Ustawienia projektu > Autoload, dodaj swój skrypt event_bus.gd i nazwij go EventBus. Automatycznie stanie się globalnie dostępnym singletonem.
Przekazywanie sygnałów (komunikacja rodzic-dziecko)
Węzeł rodzica nasłuchuje sygnałów swoich dzieci i przekazuje lub agreguje informacje. Dzieci nigdy nie muszą wiedzieć o sobie nawzajem.
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 dla sekwencyjnego przebiegu gry
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()
Dynamiczne połączenia dla tworzonych węzłów
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. Ściąga migracyjna (Godot 3 → 4)
Dodaj tę tabelę do zakładek jako szybki punkt odniesienia podczas przenoszenia projektu z Godot 3.
| Operacja | Godot 3 | Godot 4 |
|---|---|---|
| Połącz | connect("sig", obj, "method") |
sig.connect(method) |
| Rozłącz | disconnect("sig", obj, "method") |
sig.disconnect(method) |
| Wyślij | emit_signal("sig", args) |
sig.emit(args) |
| Sprawdź połączenie | is_connected("sig", obj, "method") |
sig.is_connected(method) |
| Czekaj na sygnał | yield(obj, "sig") |
await obj.sig |
| Powiąż dodatkowe argumenty | connect("sig", obj, "method", [data]) |
sig.connect(method.bind(data)) |
| Jednorazowo | connect("sig", obj, "method", [], CONNECT_ONESHOT) |
sig.connect(method, CONNECT_ONE_SHOT) |
10. Typowe błędy
1. Używanie connect opartego na łańcuchach znaków (styl Godot 3)
# 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. Używanie emit_signal() zamiast .emit()
Chociaż emit_signal() wciąż działa, omija sprawdzanie na etapie kompilacji i jest oficjalnie przestarzałe. Używaj .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. Łączenie ze zwolnionym węzłem
Jeśli połączysz sygnał z metodą na węźle, a ten węzeł zostanie zwolniony przez queue_free(), następne wysłanie sygnału spowoduje awarię. Rozwiązania:
- Rozłącz w
_exit_tree() - Użyj
CONNECT_ONE_SHOTdla jednorazowych zdarzeń - Sprawdź
is_instance_valid(target)przed wysłaniem
# 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. Zapominanie o rozłączeniu sygnałów między scenami
Połączenia z sygnałami Autoload utrzymują się między zmianami scen. Jeśli połączysz w _ready(), ale nigdy nie rozłączysz, otrzymasz błędy lub nieoczekiwane zachowanie po przeładowaniu sceny.
# 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. Łączenie tego samego sygnału dwukrotnie
Jeśli _ready() jest wywoływane wielokrotnie (np. przy zmianie rodzica węzła), możesz przypadkowo połączyć tę samą metodę dwukrotnie. Handler zostanie wtedy uruchomiony dwa razy na każde wysłanie.
# Guard against double-connection
func _ready() -> void:
if not EventBus.score_changed.is_connected(_on_score_changed):
EventBus.score_changed.connect(_on_score_changed)
Chcesz, żeby AI zarządzało architekturą twoich sygnałów?
Godot MCP Pro łączy asystenty AI takie jak Claude bezpośrednio z twoim edytorem Godot. Potrafi łączyć, rozłączać, audytować i wizualizować przepływy sygnałów w całym twoim projekcie — automatycznie.
analyze_signal_flow
find_signal_connections
connect_signal
disconnect_signal
get_signals