Señales

Introducción

Las señales son en Godot una version del "patron del observador". Estas permiten a un nodo enviar un mensaje, que otros nodos pueden escuchar y responder. Por ejemplo, en vez de chequear continuamente si un botón esta siendo presionado, el botón puede emitir una señal cuando esta presionado.

Nota

Puedes leer mas sobre el patrón del observador aquí: http://gameprogrammingpatterns.com/observer.html

Las señales son una forma de desacoplar los objetos del juego, lo que permite obtener un código mejor organizado y fácil de administrar. En lugar de forzar a los objetos del juego a esperar que otros objetos estén siempre presentes, pueden emitir señales a las que todos los objetos interesados puedan suscribirse y responder.

A continuación puedes ver algunos ejemplos de cómo puedes usar las señales en tus propios proyectos.

Ejemplo de Timer

Para ver cómo funcionan las señales, intentemos usar un nodo Timer. Creamos una nueva escena con un Node2D y dos hijos: un Timer y un Sprite. En la parte de Escenas, cambia el nombre del Node2D a TimerExample.

Para la textura del Sprite, puedes usar el icono de Godot, o cualquier otra imagen que desees. Para ello, desde el inspector del elemento Sprite, en el atributo Texture selecciona Load en el menú desplegable. Agrega un script al nodo raíz, pero no le añadas ningún código todavía.

Tu árbol de la escena debería verse así:

../../_images/signals_node_setup.png

En las propiedades del nodo Temporizador, marca la casilla "On" que aparece junto a Autostart. Esto hará que el temporizador se inicie automáticamente cuando se ejecute la escena. Puedes dejar el Wait Time en 1 segundo.

Junto a la pestaña "Inspector" hay una pestaña con el nombre "Nodos". Haz clic en esta pestaña y verás todas las señales que puede emitir el nodo seleccionado. En el caso del nodo Timer, el que nos ocupa es el "timeout". Esta señal se emite cada vez que el temporizador alcanza 0.

../../_images/signals_node_tab_timer.png

Haz clic en la señal "timeout()" y en "Connect...". Se abrirá la siguiente ventana, donde podrás definir cómo quieres conectar la señal:

../../_images/signals_connect_dialog_timer.png

En el lado izquierdo, verás los nodos de tu escena y podrás seleccionar el nodo que quieras que "escuche " la señal. Hay que tener en cuenta que el nodo Timer es azul ,es una indicación visual de que es el nodo el que está emitiendo la señal. Selecciona el nodo raíz.

Advertencia

El nodo de destino debe tener un script adjunto o recibirá un mensaje de error.

En el lado derecho, puede enlazar un número arbitrario de argumentos de (posiblemente) diferentes tipos. Esto puede ser útil cuando se tiene más de una señal conectada al mismo método, ya que cada emisión de la señal dará lugar a diferentes valores para esos argumentos adicionales llamados.

En la parte inferior de la ventana hay un campo denominado "Método Receptor". Este es el nombre de la función en el script del nodo destino que queremos usar. Por defecto, Godot creará esta función usando la convención de nombres _on_<node_name>_<signal_name> pero puedes cambiarla si lo deseas.

Haz clic en "Conectar" y verás que la función ha sido creada en el script:

extends Node2D


func _on_Timer_timeout():
    pass # Replace with function body.
public class TimerExample : Node2D
{
    public void _on_Timer_timeout()
    {
        // Replace with function body.
    }
}

Ahora podemos reemplazar el código del placeholder con cualquier código que queramos ejecutar cuando recibamos la señal. Hagamos que el Sprite parpadee:

extends Node2D


func _on_Timer_timeout():
    # Note: the `$` operator is a shorthand for `get_node()`,
    # so `$Sprite` is equivalent to `get_node("Sprite")`.
    $Sprite.visible = !$Sprite.visible
public class TimerExample : Node2D
{
    public void _on_Timer_timeout()
    {
        var sprite = GetNode<Sprite>("Sprite");
        sprite.Visible = !sprite.Visible;
    }
}

Ejecuta la escena y verás el Sprite parpadeando cada segundo. Se puede cambiar la propiedad Wait Time del temporizador para modificarla.

Conectar señales por código

También puedes hacer la conexión de la señal por código en lugar de hacerlo con el editor. Esto suele ser necesario cuando estás instanciando nodos mediante código, por lo que no puedes usar el editor para hacer la conexión.

Primero, desconecta la señal seleccionando la conexión en la pestaña "Nodo" del Timer y haciendo click en desconectar.

../../_images/signals_disconnect_timer.png

Para realizar la conexión en código, podemos usar la función connect. La colocaremos en _ready() para que la conexión se cree al iniciar la ejecución. La sintaxis de la función es <source_node>.connect(<signal_name>, <target_node>, <target_function_name>). El código para la conexión de nuestro Timer es el siguiente:

extends Node2D


func _ready():
    $Timer.connect("timeout", self, "_on_Timer_timeout")


func _on_Timer_timeout():
    $Sprite.visible = !$Sprite.visible
public class TimerExample : Node2D
{
    public override void _Ready()
    {
        GetNode("Timer").Connect("timeout", this, nameof(_on_Timer_timeout));
    }

    public void _on_Timer_timeout()
    {
        var sprite = GetNode<Sprite>("Sprite");
        sprite.Visible = !sprite.Visible;
    }
}

Señales personalizadas

También puedes declarar tus propia señales (signals) personalizadas dentro de Godot:

extends Node2D


signal my_signal
public class Main : Node2D
{
    [Signal]
    public delegate void MySignal();
}

Una vez declarada, tus señales (signals) personalizadas aparecerán en el Inspector y podrán ser conectadas de la misma forma que las señales incorporadas en un nodo.

Para emitir una señal mediante código, utiliza la función emit_signal:

extends Node2D


signal my_signal


func _ready():
    emit_signal("my_signal")
public class Main : Node2D
{
    [Signal]
    public delegate void MySignal();

    public override void _Ready()
    {
        EmitSignal(nameof(MySignal));
    }
}

Una señal también tiene la opción de declarar uno o más argumentos. Especificando los nombres de los argumentos entre paréntesis:

extends Node


signal my_signal(value, other_value)
public class Main : Node
{
    [Signal]
    public delegate void MySignal(bool value, int other_value);
}

Nota

Estos argumentos se muestran en el panel de nodos del editor, y Godot puede usarlos para generar funciones de callback para usted. Sin embargo, usted puede seguir emitiendo cualquier número de argumentos cuando emite señales, así que depende de usted emitir los valores correctos.

Para pasar valores, agrégalos como el segundo argumento de la función emit_signal:

extends Node


signal my_signal(value, other_value)


func _ready():
    emit_signal("my_signal", true, 42)
public class Main : Node
{
    [Signal]
    public delegate void MySignal(bool value, int other_value);

    public override void _Ready()
    {
        EmitSignal(nameof(MySignal), true, 42);
    }
}

Conclusión

Muchos de los tipos de nodos incorporados en Godot proporcionan señales que puedes utilizar para detectar eventos. Por ejemplo, un Area2D, representando una moneda, emite una señal del tipo body_entered cada vez que el physics body del jugador entra en su espacio de colisión (collision shape), lo que permite saber cuando el jugador la ha recolectado.

En la siguiente sección, Tu primer juego, construirás un juego completo que incluirá el uso de varias señales para conectar diferentes componentes del juego.