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 patron del observador aqui: 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 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
{
    private 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 «Nodos» del Timer y haciendo clic en desconectar.

../../_images/signals_disconnect_timer.png

Para realizar la conexión en código, podemos usar la función connect. La establecemos en _ready() para que la conexión se cree en ejecución. La sintaxis de la función es <source_node>.connect(<signal_name>, <target_node>, <target_function_name>). Aquí está el código para la conexión de nuestro Timer:

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 (signal) personalizadas dentro de Godot:

extends Node2D

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

Una vez declaradas, las señales personalizadas aparecerán en el Inspector y podrán conectarse de la misma forma que las señales integradas del nodo.

Para emitir una señal por 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));
    }
}

Conclusión

Muchos de los tipos de nodos incorporados en Godot proporcionan señales que puedes utilizar para detectar eventos. Por ejemplo, un Area2D, el cual representa una moneda, emite una señal del tipo body_entered cada vez que el cuerpo físico del jugador entra a su forma de colisión, permitiéndote saber cuando el jugador la ha recolectado.

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