CommunityProgramación y desarrollogithub.com

ziva-sh/godot-signals

Patrones de señales en Godot 4 y sus modos de fallo. Declarar señales tipadas, emit(), connect() y disconnect() con Callables, bind() y unbind(), lambdas, CONNECT_ONE_SHOT y CONNECT_DEFERRED, await en señales y temporizadores, conexiones del editor guardadas en archivos .tscn, y la regla de llamar hacia abajo, señal hacia arriba. Útil al conectar botones de UI, temporizadores, eventos de Area2D o body y eventos de juego personalizados, al reemplazar código Godot 3 como connect("signal", self, "method") o yield, o al depurar errores como "already connected".

¿Qué es godot-signals?

Esta skill proporciona una guía completa sobre el sistema de señales en Godot 4, esencial para cualquier desarrollador que trabaje con este motor. Explica cómo declarar señales tipadas, emitirlas y conectarlas usando la sintaxis moderna de objetos y Callables, reemplazando las cadenas de texto de Godot 3. También cubre funciones avanzadas como bind() y unbind() para pasar argumentos adicionales, el uso de lambdas y las banderas CONNECT_ONE_SHOT y CONNECT_DEFERRED. Incluye secciones sobre await para esperar señales y temporizadores, conexiones del editor guardadas en archivos .tscn, y patrones como "call down, signal up" para un flujo de comunicación limpio entre nodos. Es compatible con cualquier flujo de trabajo que use Godot 4, desde scripts simples hasta sistemas de eventos complejos con autoloads. La skill ayuda a evitar errores comunes como conexiones duplicadas, falta de coincidencia de argumentos o pérdida de conexiones al renombrar métodos.

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

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Señales en Godot 4

En Godot 4, una señal es un valor Signal de primera clase y una referencia a un método es un Callable. Usa la sintaxis de objeto (button.pressed.connect(_on_pressed)) en todas partes. El analizador sintáctico rechazará un manejador mal escrito, así como una señal mal escrita en self; una señal mal escrita en otro nodo tipado se detecta cuando unsafe_property_access está configurado como Error (consulta la skill godot-4-api-pitfalls). Las formas de cadena (connect("pressed", ...), emit_signal("died")) solo se verifican en tiempo de ejecución.

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")

El manejador debe aceptar exactamente los argumentos que emite la señal. Una discrepancia no es un error de análisis sintáctico; al momento de emitir, Godot registra Error calling from signal 'x' to callable: '...': Method expected 0 argument(s), but called with 1. y omite el manejador.

bind() y unbind()

  • bind(a, b) añade argumentos extra después de los propios de la señal. Un manejador para pressed enlazado con un valor toma un parámetro; para health_changed(old, new) enlazado con un valor toma tres, en el orden old, new, bound.
  • unbind(n) elimina los últimos n argumentos de la señal, permitiendo que una función sin argumentos maneje una señal que envía algunos.
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, duplicados y conexiones de un solo uso

  • disconnect() necesita un Callable igual al conectado. _handler.bind(1) construido de nuevo se compara como igual y funciona. Una lambda no: guárdala en una variable y desconéctala usando esa variable.
  • Conectar el mismo Callable dos veces registra Signal 'x' is already connected to given callable ... y mantiene una conexión. Protégete con is_connected() cuando el código pueda ejecutarse dos veces, por ejemplo en _ready() de un nodo que se vuelve a agregar al árbol.
  • connect(callable, CONNECT_ONE_SHOT) se desconecta después de la primera emisión. CONNECT_DEFERRED ejecuta el manejador en tiempo de inactividad. Úsalo (o set_deferred()) cuando un callback de física como body_entered cambie el estado de la física; hacerlo directamente registra Can't change this state while flushing queries.
  • Las conexiones a un objeto se eliminan cuando ese objeto es 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 la función hasta que la señal se active y devuelve sus argumentos: null para ningún argumento, el valor para uno, un Array para dos o más. Una función que contiene await es una corrutina; los llamadores que necesitan su resultado también deben hacer await, de lo contrario continúan inmediatamente.

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. Si la señal nunca se emite (o su emisor se libera primero), la función simplemente se detiene allí, sin error. Usa un Timer o una conexión de un solo uso cuando se necesite una ruta alternativa.

Conexiones del editor

Las conexiones hechas en el dock de Nodos se guardan en el archivo de la escena, no en el script:

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

Si el método se renombra o elimina en el script, la conexión permanece en el .tscn. En Godot 4.7.2, emitir una señal cuyo método conectado no existe no imprime ningún error, por lo que el botón simplemente deja de funcionar. Después de renombrar un manejador, busca en las escenas method="old_name" y actualízalas, o reconecta en el dock de Nodos. Conecta en código cuando el script debe ser autocontenido; conecta en el editor cuando los diseñadores cablean las escenas. Evita hacer ambas cosas para la misma señal y método: la segunda conexión registrará "already connected".

Llamar hacia abajo, señal hacia arriba

Un nodo llama a métodos en sus hijos (los posee y sabe que existen) y emite señales para que su padre o hermanos reaccionen (no debe saber quién está escuchando). El padre, o cualquier nodo que componga los dos, realiza la conexión:

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 padre solo conoce a Player como un Node2D, por lo que nombra la señal personalizada como una cadena. Dale al script del jugador un class_name Player, tipifica la variable como Player, y escribe player.health_changed.connect(_on_player_health_changed) para que el analizador también verifique el nombre.

Para eventos de los que muchas escenas no relacionadas se preocupan (el jugador murió, el nivel terminó), un patrón común es un script de autoload que solo declara señales, usado como un bus de eventos: Events.player_died.emit() y Events.player_died.connect(_on_player_died).

Formas de Godot 3 para reemplazar

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") o 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

Skills relacionados