1. Introducción — Qué cambió en Godot 4
Godot 4 renovó por completo el funcionamiento de las señales. Si vienes de Godot 3, la vieja sintaxis basada en cadenas object.connect("signal_name", target, "method_name") ya no existe. En su lugar, Godot 4 utiliza una API basada en Callable que es segura respecto a tipos, cómoda para refactorizar y que detecta errores en tiempo de compilación en vez de en tiempo de ejecución.
Esta guía cubre todo lo que necesitas saber sobre las señales en Godot 4.4+: declararlas, conectarlas, desconectarlas, emitirlas, esperarlas (await) y los patrones más útiles del mundo real.
En Godot 4, las señales son objetos de primera clase. Accedes a ellas como propiedades (p. ej. button.pressed) y llamas a métodos sobre ellas (.connect(), .emit(), .disconnect()). Se acabó la API basada en cadenas.
2. Declarar señales personalizadas
Declara las señales al principio de tu script con la palabra clave signal. Opcionalmente puedes especificar nombres y tipos de parámetros, para documentación y para el autocompletado del 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)
Añade siempre anotaciones de tipo a los parámetros de las señales. Esto activa el autocompletado en el editor y hace que tu código se documente a sí mismo. Las señales con anotaciones de tipo también muestran la información de sus parámetros en la pestaña Signals del panel Node.
3. Conectar señales (la nueva forma)
El mayor cambio en Godot 4 es la manera de conectar señales. En lugar de pasar cadenas, usas la señal como una propiedad y llamas a .connect() con un Callable (una referencia a una función).
Conexión 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!")
Conexión con argumentos adicionales mediante bind()
Usa .bind() para pasar datos adicionales junto con la señal. Es útil cuando conectas varias señales al mismo 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)
Conexiones con lambda (en línea)
Para manejadores cortos puedes usar directamente una función lambda. Así la lógica sencilla queda cerca del punto de conexión.
# 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!")
)
Las conexiones con lambda no se pueden desconectar fácilmente, porque no tienes una referencia a la función anónima. Si necesitas desconectar más tarde, guarda el Callable en una variable o usa en su lugar un método con nombre.
Conectar en el editor (panel Node)
También puedes conectar señales a través de la interfaz del editor de Godot. Selecciona un nodo, abre la pestaña Signals del panel Node, haz doble clic en una señal y elige el nodo y el método de destino. El editor generará automáticamente un método como _on_button_pressed() en tu script. Es idéntico a llamar a .connect() en el código — solo te ahorra escribir.
4. Desconectar señales
Desconecta una señal cuando ya no quieras recibirla. Esto es importante para evitar errores cuando se liberan nodos o cuando cambias de estado de juego.
# 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)
En las conexiones de señales entre escenas (p. ej. a un Autoload), desconecta siempre en _exit_tree() para evitar referencias colgantes cuando se libera el nodo:
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. Emitir señales
En Godot 4 usas .emit() en lugar del viejo emit_signal(). La señal es un objeto, así que llamas al método directamente sobre ella.
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
El viejo emit_signal("signal_name") técnicamente sigue funcionando en Godot 4, pero está obsoleto. Prefiere siempre signal_name.emit() para el código nuevo. La variante basada en cadenas podría eliminarse en una futura versión de Godot.
6. Esperar señales (await)
Godot 4 reemplazó yield() por await. Esto te permite pausar una función hasta que se dispare una señal — perfecto para cinemáticas, tutoriales, animaciones secuenciales y 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!")
Obtener valores de señales esperadas con await
Si la señal emite argumentos, await los devuelve. Con un único argumento obtienes el valor directamente. Con varios argumentos obtienes un 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 conexión de señales
Godot ofrece flags de conexión como segundo argumento de .connect(), con los que puedes modificar el comportamiento de la conexión.
CONNECT_ONE_SHOT
La conexión se elimina automáticamente después de que la señal se dispare una vez. Perfecto para eventos únicos como animaciones de muerte o el desbloqueo de logros.
# 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
El método conectado se llama al final del fotograma actual (durante el tiempo de inactividad) en lugar de inmediatamente. Es útil cuando el manejador de la señal modifica el árbol de escena, algo que no es seguro hacer durante el procesamiento de física o de señales.
# 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()
Combinar flags
# One-shot AND deferred
trigger.body_entered.connect(_on_trigger, CONNECT_ONE_SHOT | CONNECT_DEFERRED)
8. Patrones comunes
EventBus (hub global de señales)
El patrón EventBus usa un singleton Autoload para desacoplar nodos. Cualquier nodo puede emitir o escuchar eventos globales sin necesitar una referencia directa a otro nodo. Es uno de los patrones más potentes de 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()
Ve a Proyecto > Ajustes del proyecto > Autoload, añade tu script event_bus.gd y ponle el nombre EventBus. Se convierte automáticamente en un singleton accesible de forma global.
Relé de señales (comunicación padre-hijo)
Un nodo padre escucha las señales de sus hijos y transmite o agrega información. Los hijos nunca necesitan conocerse entre sí.
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 un flujo de juego secuencial
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()
Conexiones dinámicas para nodos 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. Chuleta de migración (Godot 3 → 4)
Guarda esta tabla como referencia rápida al portar tu proyecto de Godot 3.
| Operación | 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) |
| Comprobar conexión | is_connected("sig", obj, "method") |
sig.is_connected(method) |
| Esperar una señal | yield(obj, "sig") |
await obj.sig |
| Vincular argumentos adicionales | 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. Errores comunes
1. Usar connect basado en cadenas (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() en lugar de .emit()
Aunque emit_signal() todavía funciona, evita las comprobaciones en tiempo de compilación y está oficialmente obsoleto. Usa .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 un nodo liberado
Si conectas una señal a un método de un nodo y ese nodo se libera con queue_free(), la siguiente emisión de la señal provocará un fallo. Soluciones:
- Desconectar en
_exit_tree() - Usar
CONNECT_ONE_SHOTpara eventos únicos - Comprobar con
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. Olvidar desconectar señales entre escenas
Las conexiones a señales de Autoload persisten a través de los cambios de escena. Si conectas en _ready() pero nunca desconectas, tendrás errores o comportamientos inesperados cuando se recargue la escena.
# 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 la misma señal dos veces
Si _ready() se llama varias veces (p. ej. al reasignar el padre de un nodo), puedes conectar accidentalmente el mismo método dos veces. Entonces el manejador se disparará dos veces por cada emisión.
# Guard against double-connection
func _ready() -> void:
if not EventBus.score_changed.is_connected(_on_score_changed):
EventBus.score_changed.connect(_on_score_changed)
¿Quieres que la IA gestione la arquitectura de tus señales?
Godot MCP Pro conecta asistentes de IA como Claude directamente con tu editor de Godot. Puede conectar, desconectar, auditar y visualizar los flujos de señales de todo tu proyecto — de forma automática.
analyze_signal_flow
find_signal_connections
connect_signal
disconnect_signal
get_signals