En Godot, una señal puede entenderse como una notificación emitida por un nodo.
Algo ocurrió. Quien esté interesado puede responder.
Ejemplos:
- Botón: me han pulsado.
- Moneda: el jugador me recogió.
- Jugador: mi vida cambió.
- Enemigo: morí.
- Temporizador: se acabó el tiempo.
Otros nodos pueden conectarse a estas señales y ejecutar una función cuando se emiten. El emisor no necesita buscar directamente al receptor, lo que reduce el acoplamiento entre nodos.
Los tres pasos básicos
Un flujo completo de señales suele tener tres pasos:
- Declarar la señal.
- Conectar la señal.
- Emitir la señal.
Puedes imaginarlo como un timbre:
| Concepto | Analogía |
|---|---|
signal |
Instalar el timbre |
connect |
Conectar el timbre al sonido interior |
emit |
Pulsar el timbre |
| Función receptora | Alguien lo oye y abre la puerta |
La forma sencilla de recordarlo:
|
|
Dos tipos de señales en Godot
En Godot, las señales habituales se dividen en señales integradas y señales personalizadas.
Señales integradas
Las señales integradas ya vienen con los nodos de Godot.
Ejemplos comunes:
Button.pressedTimer.timeoutArea2D.body_enteredArea2D.body_exited
Si el nodo de moneda hereda de Area2D, ya tiene la señal body_entered. Cuando un CharacterBody2D entra en el área de colisión de la moneda, Godot emite esa señal automáticamente.
Señales personalizadas
Las señales personalizadas las declaramos nosotros.
Por ejemplo, una moneda recogida:
|
|
Esta señal collected no viene integrada en Godot. Es un tipo de notificación que definimos nosotros.
Dos capas de señales en el ejemplo de la moneda
Un script simple para la moneda puede ser:
|
|
Aquí se usan realmente dos capas de señales.
Primera capa: body_entered
Esta línea:
|
|
significa:
Cuando un objeto físico entra en el
Area2Dde Coin, llama a_on_body_entered().
Flujo:
|
|
El parámetro body es el nodo que entró en el área de la moneda. Si entra Player:
|
|
Segunda capa: collected
Esta línea:
|
|
declara una señal, indicando que Coin puede enviar una notificación llamada collected.
Después:
|
|
significa emitir formalmente la notificación:
Esta moneda ya fue recogida.
Pero cuidado: emitir una señal no significa que alguien la reciba.
Si ningún nodo está conectado a collected, la señal se emite igualmente, pero no produce otros efectos. La moneda desaparece por esta línea:
|
|
No desaparece automáticamente por collected.emit().
Flujo completo de comunicación
Cuando el jugador toca la moneda, el flujo completo es:
|
|
Aquí hay dos emisores:
|
|
body_entered le dice a Coin: “Algo entró en tu área.”
collected le dice a Main: “Esta moneda fue recogida.”
Cómo hacer que Main reciba la señal de la moneda
Supongamos esta estructura de escena:
|
|
Puedes conectar la señal en el editor o desde código.
Método 1: conectar en el editor de Godot
Abre main.tscn.
Primero, selecciona en el árbol de escena:
|
|
Luego, en el panel derecho, cambia de Inspector a:
|
|
En la interfaz de Godot, este panel aparece como:
|
|
Busca la señal personalizada:
|
|
Haz doble clic.
Elige como nodo receptor:
|
|
Pulsa Connect. Godot generará una función parecida en el script de Main:
|
|
Cámbiala por:
|
|
Al ejecutar y recoger la moneda, verás:
|
|
Método 2: conectar señales desde código
También puedes conectarla directamente en main.gd:
|
|
La línea importante es:
|
|
Significa:
|
|
En Godot 4 se recomienda llamar connect() directamente desde el objeto Signal:
|
|
No copies sin revisar la sintaxis antigua de algunos tutoriales de Godot 3.
Hacer que una señal lleve datos
Una señal no solo puede decir “algo ocurrió”; también puede llevar datos.
Por ejemplo, distintas monedas pueden tener distinto valor:
|
|
Una moneda normal puede usar:
|
|
Una moneda grande puede usar:
|
|
Main también debe recibir el parámetro:
|
|
El proceso:
|
|
Por qué Coin no debería modificar directamente la UI
No se recomienda escribir esto:
|
|
Porque Coin dependería de la estructura exacta de nodos de Main:
- Coin debe saber dónde está
ScoreLabel. - Coin debe saber cómo se guarda la puntuación.
- Coin debe saber cómo se actualiza la UI.
Si mueves el nodo de UI, la ruta de Coin puede romperse.
Una mejor estructura:
|
|
Es decir:
|
|
Esa es la función de las señales: reducir el acoplamiento entre nodos.
Tres conceptos fáciles de confundir
signal
Declara una señal:
|
|
Equivale a crear un tipo de notificación.
connect()
Indica qué función se llamará cuando la señal se emita:
|
|
emit()
Emite la señal:
|
|
Recordatorio:
|
|
Código recomendado para la moneda
coin.gd:
|
|
main.gd:
|
|
La idea central es:
Las señales notifican qué ocurrió. No deciden todo lo que los demás nodos deben hacer después.
Cuando entiendes esto, botones, monedas, enemigos, temporizadores, cambios de vida y actualizaciones de UI en Godot se vuelven mucho más fáciles de organizar.