1. Introdução — O que mudou no Godot 4
O Godot 4 reformulou completamente o funcionamento dos sinais. Se você vem do Godot 3: a antiga sintaxe baseada em string object.connect("signal_name", target, "method_name") não existe mais. Em seu lugar, o Godot 4 usa uma API baseada em Callable, que é type-safe, favorável a refatorações e detecta erros em tempo de compilação em vez de em tempo de execução.
Este guia cobre tudo o que você precisa saber sobre sinais no Godot 4.4+: declarar, conectar, desconectar, emitir, aguardar com await e os padrões mais úteis do mundo real.
No Godot 4, os sinais são objetos de primeira classe. Você os acessa como propriedades (por exemplo, button.pressed) e chama métodos neles (.connect(), .emit(), .disconnect()). Chega de API baseada em string.
2. Declarando sinais personalizados
Declare sinais no início do seu script com a palavra-chave signal. Opcionalmente, você pode especificar nomes e tipos de parâmetros, para documentação e para o autocompletar do 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)
Sempre adicione anotações de tipo aos parâmetros dos sinais. Isso habilita o autocompletar no editor e torna seu código autodocumentado. Sinais com anotações de tipo também exibem informações dos parâmetros na aba Signals do dock de nós.
3. Conectando sinais (a nova forma)
A maior mudança no Godot 4 é a forma de conectar sinais. Em vez de passar strings, você usa o sinal como uma propriedade e chama .connect() com um Callable (uma referência a uma função).
Conexão básica
# 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!")
Conexão com argumentos extras via bind()
Use .bind() para passar dados extras junto com o sinal. Isso é útil quando você conecta vários sinais ao mesmo método.
# 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)
Conexões lambda (inline)
Para handlers curtos, você pode usar uma função lambda diretamente. Isso mantém a lógica simples próxima do ponto de conexão.
# 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!")
)
Conexões lambda não podem ser desconectadas facilmente, porque você não tem uma referência à função anônima. Se precisar desconectar mais tarde, armazene o Callable em uma variável ou use um método nomeado.
Conectando no editor (dock de nós)
Você ainda pode conectar sinais pela interface do editor do Godot. Selecione um nó, abra a aba Signals do dock de nós, dê um duplo clique em um sinal e escolha o nó e o método de destino. O editor gera automaticamente um método como _on_button_pressed() no seu script. Isso é idêntico a chamar .connect() no código — apenas economiza digitação.
4. Desconectando sinais
Desconecte um sinal quando não quiser mais recebê-lo. Isso é importante para evitar erros quando nós são liberados ou quando o estado do jogo muda.
# 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)
Para conexões de sinais entre cenas (por exemplo, conectar a um Autoload), sempre desconecte em _exit_tree() para evitar referências pendentes quando o nó for liberado:
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. Emitindo sinais
No Godot 4, use .emit() em vez do antigo emit_signal(). O sinal é um objeto, então você chama o método diretamente nele.
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
O antigo emit_signal("signal_name") tecnicamente ainda funciona no Godot 4, mas está obsoleto. Prefira sempre signal_name.emit() em código novo. A versão baseada em string pode ser removida em um lançamento futuro do Godot.
6. Aguardando sinais (await)
O Godot 4 substituiu yield() por await. Isso permite pausar uma função até que um sinal seja emitido — perfeito para cutscenes, tutoriais, animações sequenciais e eventos temporizados.
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!")
Obtendo valores de sinais aguardados
Se o sinal emitir argumentos, await os retorna. Com um único argumento, você recebe o valor diretamente. Com múltiplos argumentos, você recebe um 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. Flags de conexão de sinais
O Godot oferece flags de conexão como segundo argumento de .connect(), que permitem modificar o comportamento da conexão.
CONNECT_ONE_SHOT
A conexão é removida automaticamente depois que o sinal é emitido uma vez. Perfeito para eventos únicos, como animações de morte ou desbloqueio de conquistas.
# 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
O método conectado é chamado ao final do frame atual (durante o tempo ocioso) em vez de imediatamente. Útil quando o handler do sinal modifica a árvore de cena, o que não é seguro fazer durante o processamento de física ou de sinais.
# 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()
Combinando flags
# One-shot AND deferred
trigger.body_entered.connect(_on_trigger, CONNECT_ONE_SHOT | CONNECT_DEFERRED)
8. Padrões comuns
EventBus (hub global de sinais)
O padrão EventBus usa um singleton Autoload para desacoplar nós. Qualquer nó pode emitir ou escutar eventos globais sem precisar de uma referência direta a outro nó. Este é um dos padrões mais poderosos do 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()
Vá em Projeto > Configurações do Projeto > Autoload, adicione seu script event_bus.gd e dê a ele o nome EventBus. Ele se torna automaticamente um singleton acessível globalmente.
Repasse de sinais (comunicação pai-filho)
Um nó pai escuta os sinais de seus filhos e repassa ou agrega informações. Os filhos nunca precisam saber uns dos outros.
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 para fluxo de jogo sequencial
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()
Conexões dinâmicas para nós instanciados
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. Guia rápido de migração (Godot 3 → 4)
Salve esta tabela como referência rápida ao portar seu projeto do Godot 3.
| Operação | Godot 3 | Godot 4 |
|---|---|---|
| Conectar | connect("sig", obj, "method") |
sig.connect(method) |
| Desconectar | disconnect("sig", obj, "method") |
sig.disconnect(method) |
| Emitir | emit_signal("sig", args) |
sig.emit(args) |
| Verificar conexão | is_connected("sig", obj, "method") |
sig.is_connected(method) |
| Aguardar sinal | yield(obj, "sig") |
await obj.sig |
| Vincular argumentos extras | 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. Erros comuns
1. Usar connect baseado em string (estilo 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. Usar emit_signal() em vez de .emit()
Embora emit_signal() ainda funcione, ele ignora as verificações em tempo de compilação e está oficialmente obsoleto. Use .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. Conectar a um nó liberado
Se você conectar um sinal a um método em um nó e esse nó for liberado com queue_free(), a próxima emissão do sinal causará um crash. Soluções:
- Desconectar em
_exit_tree() - Usar
CONNECT_ONE_SHOTpara eventos únicos - Verificar com
is_instance_valid(target)antes de emitir
# 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. Esquecer de desconectar sinais entre cenas
Conexões a sinais de Autoload persistem entre trocas de cena. Se você conectar em _ready() mas nunca desconectar, terá erros ou comportamento inesperado quando a cena for recarregada.
# 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. Conectar o mesmo sinal duas vezes
Se _ready() for chamado várias vezes (por exemplo, ao reparentar um nó), você pode acabar conectando o mesmo método duas vezes sem querer. O handler passará a disparar duas vezes por emissão.
# Guard against double-connection
func _ready() -> void:
if not EventBus.score_changed.is_connected(_on_score_changed):
EventBus.score_changed.connect(_on_score_changed)
Quer que a IA gerencie a arquitetura de sinais do seu jogo?
O Godot MCP Pro conecta assistentes de IA como o Claude diretamente ao seu editor Godot. Ele pode conectar, desconectar, auditar e visualizar fluxos de sinais em todo o seu projeto — automaticamente.
analyze_signal_flow
find_signal_connections
connect_signal
disconnect_signal
get_signals