ziva-sh/godot-signals

Padrões de sinais do Godot 4 e seus modos de falha - declarando sinais tipados, emit(), connect() e disconnect() com Callables, bind() e unbind(), lambdas, CONNECT_ONE_SHOT e CONNECT_DEFERRED, await em sinais e timers, conexões feitas no editor armazenadas em arquivos .tscn, e a regra call down, signal up. Use ao conectar botões de UI, timers, eventos de Area2D ou body e eventos de jogo personalizados, ao substituir código Godot 3 como connect("signal", self, "method") ou yield, ou ao depurar erros como "already connected".

O que é godot-signals?

Este skill documenta de forma abrangente o sistema de sinais do Godot 4, explicando como declarar, emitir, conectar e desconectar sinais usando a sintaxe moderna baseada em objetos e callables, ao invés das strings do Godot 3. Ele detalha a criação de sinais tipados, o uso de bind() e unbind() para passar argumentos extras, conexões únicas (ONE_SHOT) e adiadas (DEFERRED), além de mostrar como usar await em sinais e timers. O material cobre também as armadilhas comuns: handlers com assinaturas erradas, lambdas que não podem ser desconectadas sem manter a referência da variável, duplicação involuntária de conexões, e a regra arquitetural "call down, signal up" (chame para baixo, sinalize para cima). Por fim, inclui uma tabela de conversão dos métodos antigos do Godot 3 (connect, emit_signal, yield) para os equivalentes do Godot 4. É útil para desenvolvedores que estão migrando para o Godot 4 ou querem escrever código de sinais mais seguro, evitando bugs comuns e organizando melhor a comunicação entre nós.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ziva-sh/godot-skills/tree/HEAD/skills/godot-signals

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Sinais no Godot 4

No Godot 4, um sinal é um valor Signal de primeira classe e uma referência a método é um Callable. Use a sintaxe de objeto (button.pressed.connect(_on_pressed)) em todos os lugares. O analisador então rejeita um handler com nome incorreto e um sinal com nome incorreto em self; um sinal com nome incorreto em outro nó tipado é detectado quando unsafe_property_access está definido como Erro (veja o skill godot-4-api-pitfalls). As formas com string (connect("pressed", ...), emit_signal("died")) são verificadas apenas em tempo de execução.

Declarar, emitir, conectar

extends Node

signal health_changed(old_value: int, new_value: int)
signal died

var health := 100


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

@onready var start_button: Button = %StartButton
@onready var quit_button: Button = %QuitButton


func _ready() -> void:
	start_button.pressed.connect(_on_start_pressed)
	quit_button.pressed.connect(get_tree().quit)
	start_button.mouse_entered.connect(func() -> void: start_button.grab_focus())


func _on_start_pressed() -> void:
	get_tree().change_scene_to_file("res://level_1.tscn")

O handler deve aceitar exatamente os argumentos que o sinal emite. Uma incompatibilidade não é um erro de análise; no momento da emissão, o Godot registra Error calling from signal 'x' to callable: '...': Method expected 0 argument(s), but called with 1. e ignora o handler.

bind() e unbind()

  • bind(a, b) anexa argumentos extras após os próprios argumentos do sinal. Um handler para pressed vinculado com um valor recebe um parâmetro; para health_changed(old, new) vinculado com um valor, recebe três, na ordem old, new, bound.
  • unbind(n) descarta os últimos n argumentos do sinal, para que uma função sem argumentos possa tratar um sinal que envia alguns.
extends HBoxContainer

signal slot_chosen(index: int)

var slot_buttons: Array[Button] = []


func _ready() -> void:
	for i in 4:
		var button := Button.new()
		button.text = "Slot %d" % (i + 1)
		add_child(button)
		slot_buttons.append(button)
		button.pressed.connect(_on_slot_pressed.bind(i))
	var timer := Timer.new()
	add_child(timer)
	timer.timeout.connect(_refresh)
	slot_chosen.connect(_refresh.unbind(1))
	timer.start(1.0)


func _on_slot_pressed(index: int) -> void:
	slot_chosen.emit(index)


func _refresh() -> void:
	for button in slot_buttons:
		button.disabled = false

Desconectar, duplicatas e conexões únicas

  • disconnect() precisa de um Callable igual ao que foi conectado. _handler.bind(1) construído novamente é comparado como igual e funciona. Uma lambda não: mantenha-a em uma variável e desconecte essa variável.
  • Conectar o mesmo Callable duas vezes registra Signal 'x' is already connected to given callable ... e mantém uma conexão. Proteja com is_connected() quando o código puder ser executado duas vezes, por exemplo, em _ready() de um nó que é readicionado à árvore.
  • connect(callable, CONNECT_ONE_SHOT) desconecta após a primeira emissão. CONNECT_DEFERRED executa o handler em tempo ocioso. Use-o (ou set_deferred()) quando um callback de física como body_entered altera o estado da física; fazê-lo diretamente registra Can't change this state while flushing queries.
  • Conexões a um objeto são removidas quando esse objeto é liberado.
extends Area2D

@onready var shape: CollisionShape2D = $CollisionShape2D
var _on_first_touch := func(body: Node2D) -> void: print(body.name, " touched first")


func _ready() -> void:
	body_entered.connect(_on_first_touch, CONNECT_ONE_SHOT)
	body_entered.connect(_close_after_entry, CONNECT_DEFERRED)
	if not body_entered.is_connected(_on_body_entered):
		body_entered.connect(_on_body_entered)


func _on_body_entered(body: Node2D) -> void:
	print(body.name, " entered")


func _close_after_entry(_body: Node2D) -> void:
	shape.disabled = true


func stop_listening() -> void:
	body_entered.disconnect(_on_body_entered)

await

await signal pausa a função até que o sinal dispare e retorna seus argumentos: null para nenhum argumento, o valor para um, um Array para dois ou mais. Uma função que contém await é uma corrotina; chamadores que precisam de seu resultado devem await também, caso contrário, continuam imediatamente.

extends Node2D

signal door_opened(door_name: String)


func _ready() -> void:
	await get_tree().create_timer(0.5).timeout
	var opened_by: String = await door_opened
	print("Door opened: ", opened_by)
	await play_intro()
	print("Intro finished")


func play_intro() -> void:
	var tween := create_tween()
	tween.tween_property(self, "modulate:a", 1.0, 1.0).from(0.0)
	await tween.finished

await nunca expira. Se o sinal nunca for emitido (ou seu emissor for liberado primeiro), a função simplesmente para ali, sem erro. Use um Timer ou uma conexão única quando um caminho alternativo for necessário.

Conexões do editor

Conexões feitas no dock de Nós são salvas no arquivo da cena, não no script:

[connection signal="pressed" from="StartButton" to="." method="_on_start_button_pressed"]

Se o método for renomeado ou excluído no script, a conexão permanece no .tscn. No Godot 4.7.2, emitir um sinal cujo método conectado não existe não imprime erro, então o botão simplesmente para de funcionar. Após renomear um handler, procure nas cenas por method="old_name" e atualize-os, ou reconecte no dock de Nós. Conecte em código quando o script deve ser autocontido; conecte no editor quando designers montam cenas. Evite fazer ambos para o mesmo sinal e método: a segunda conexão registra "already connected".

Chame para baixo, sinalize para cima

Um nó chama métodos em seus filhos (ele os possui e sabe que existem) e emite sinais para seu pai ou irmãos reagirem (ele não deve saber quem está ouvindo). O pai, ou qualquer nó que compõe os dois, faz a conexão:

extends Node2D

@onready var player: Node2D = $Player
@onready var health_bar: ProgressBar = $HUD/HealthBar


func _ready() -> void:
	player.connect(&"health_changed", _on_player_health_changed)


func _on_player_health_changed(_old_value: int, new_value: int) -> void:
	health_bar.value = new_value

Este pai conhece Player apenas como um Node2D, então nomeia o sinal personalizado como uma string. Dê ao script do jogador um class_name Player, digite a variável como Player e escreva player.health_changed.connect(_on_player_health_changed) para que o analisador também verifique o nome.

Para eventos que muitas cenas não relacionadas se importam (jogador morreu, nível concluído), um padrão comum é um script autoload que apenas declara sinais, usado como um barramento de eventos: Events.player_died.emit() e Events.player_died.connect(_on_player_died).

Formas do Godot 3 a serem substituídas

Godot 3Godot 4
connect("pressed", self, "_on_pressed")pressed.connect(_on_pressed)
connect("pressed", self, "_on_pressed", [id])pressed.connect(_on_pressed.bind(id))
connect("timeout", self, "_on_timeout", [], CONNECT_ONESHOT)timeout.connect(_on_timeout, CONNECT_ONE_SHOT)
disconnect("pressed", self, "_on_pressed")pressed.disconnect(_on_pressed)
is_connected("pressed", self, "_on_pressed")pressed.is_connected(_on_pressed)
emit_signal("died")died.emit()
yield(obj, "signal_name")await obj.signal_name
funcref(obj, "method")Callable(obj, "method") ou obj.method

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

ziva-sh/godot-4-api-pitfalls

Fixes Godot 3 APIs and GDScript syntax that AI assistants write into Godot 4 projects, such as KinematicBody2D, move_and_slide(velocity), export, onready, setget, yield, string connect(), instance(), rand_range, the Tween node, File, change_scene, Spatial, rect_* properties, Pool*Array, OS time functions and the old TileMap API. Use when writing, reviewing or porting GDScript for Godot 4.x, when a script fails with "not declared", "not found in base", "was removed in Godot 4" or "is not present

ziva-sh/godot-control-layout

Builds Godot 4 Control UIs that resize instead of clipping or overlapping - containers versus anchors, anchor presets from code, size flags, custom_minimum_size, MarginContainer margins, Label wrapping, mouse_filter, CanvasLayer HUDs, Godot 4 theme override names (add_theme_color_override and friends) and the display/window/stretch settings for multiple resolutions. Use when creating or fixing menus, HUDs, dialogs and inventory screens, when UI elements are cut off, zero-sized, overlapping, igno

ziva-sh/godot-scene-organization

Structures Godot 4 projects so scenes stay reusable and references do not break - when to make a scene, a script or a Resource, instancing with @export PackedScene, preload and load, @onready and %UniqueName node access, _ready order, groups, autoloads, class_name, and resource UIDs with the .uid files added in Godot 4.4, including how to move or rename files safely. Use when creating new scenes or scripts, deciding how nodes find each other, adding global state or an event bus, moving or renami

ziva-sh/godot-tilemaplayer

Correct Godot 4.3+ TileMapLayer code - set_cell(coords, source_id, atlas_coords), reading cells and custom data, erasing, coordinate conversion, terrains, tile collision through TileSet physics layers, finding the tile a body hit, and migrating from the deprecated TileMap node and its layer-index API. Use when placing, reading or erasing tiles from GDScript, building a TileSet in code, generating levels procedurally, handling tile collisions, or porting TileMap code from Godot 3 or Godot 4.0 to

Habilidades Relacionadas