Padrões de Sinais no Godot 4 — Guia Completo

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.

Ponto-chave

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.

player.gd
# 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)
Dica

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

GDScript
# 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.

GDScript
# 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.

GDScript
# 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!")
)
Aviso

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.

GDScript
# 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)
Boa prática

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:

GDScript
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.

player.gd
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
Observação

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.

GDScript
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.

GDScript
# 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.

GDScript
# 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.

GDScript
# 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

GDScript
# 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.

event_bus.gd (Autoload)
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
player.gd (emitter)
func die() -> void:
    # Any script can emit global signals
    EventBus.player_died.emit()
hud.gd (listener)
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()
Configurando o Autoload

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.

inventory.gd
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

level_manager.gd
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

enemy_spawner.gd
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)

GDScript
# 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().

GDScript
# 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_SHOT para eventos únicos
  • Verificar com is_instance_valid(target) antes de emitir
GDScript
# 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.

GDScript
# 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.

GDScript
# 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
Obtenha o Godot MCP Pro — $15