Segnali

Introduzione

I segnali sono la versione di Godot degli observer pattern. Consentono ad un nodo di inviare un messaggio che un altro nodo può ascoltare e rispondere. Per esempio, piuttosto che controllare in continuazione un bottone per verificare se è premuto, il bottone può emettere un segnale quando è premuto.

Nota

Puoi scoprire di più relativamente agli observer pattern qui: http://gameprogrammingpatterns.com/observer.html

I segnali sono un modo per disaccoppiare i tuoi oggetti di gioco, il che comporta un codice meglio organizzato e più gestibile. Al posto di forzare gli oggetti di gioco ad aspettarsi che altri oggetti siano sempre presenti, possono invece emettere segnali che tutti gli oggetti interessati possono ascoltare e rispondere.

Sotto puoi vedere alcuni esempi di come puoi usare i segnali nei tuoi progetti.

Esempio di Timer

To see how signals work, let’s try using a Timer node. Create a new scene with a Node2D and two children: a Timer and a Sprite. In the Scene dock, rename Node2D to TimerExample.

Per la texture dello Sprite, puoi usare l’icona di Godot, o qualsiasi altra immagine ti piaccia. Puoi farlo selezionando Load tra gli attibuti nel menu a tendina della Texture del nodo Sprite. Attacca uno script al nodo radice, ma non aggiungere nessun codice per ora.

Il tuo albero di scena dovrebbe essere simile a questo:

../../_images/signals_node_setup.png

Nelle proprietà del nodo Timer, spunta la casella «On» vicino a Autostart. Questo farà in modo che il timer parta automaticamente quando viene avviata la scena. Puoi lasciare Wait Time impostato ad un secondo.

Vicino alla tab «Inspector» ne è presente una chiamata «Node». Clicca questa tab e vedrai tutti i segnali che il nodo selezionato può emettere. Nel caso del nodo Timer, quello che c’interessa è «timeout». Questo segnale viene emesso ogni volta il Timer raggiunge 0.

../../_images/signals_node_tab_timer.png

Clicca sul segnale «timeout()» e poi clicca «Connect…» situato nella parte inferiore del pannello dei segnali. Vedrai la seguente finestra, dove puoi definire come connettere il segnale:

../../_images/signals_connect_dialog_timer.png

Nella parte sinistra, vedrai i nodi presenti nella scena e potrai selezionare quale vuoi che «ascolti» per il segnale. Nota che il nodo Timer è evidenziato in blu, proprio per indicare quale nodo sta attualmente emettendo il segnale. Seleziona il nodo base.

Avvertimento

Il nodo target deve avere uno script attaccato altrimenti riceverai un messaggio di errore.

On the right side, you can bind an arbitrary number of arguments of (possibly) different types. This can be useful when you have more than one signal connected to the same method, as each signal propagation will result in different values for those extra call arguments.

Nella parte bassa della finestra c’è un campo di testo «Metodo Ricevitore». Questo è il nome della funzione che vuoi usare nello script del nodo target. Di default, Godot creerà questa funzione usando le convenzioni _on_<node_name>_<signal_name> ma puoi cambiarlo se vuoi.

Clicca «Connetti» e vedrai che la funzione è stata creata nello 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.
    }
}

Ora possiamo sostituire il codice segnaposto con qualsiasi codice vogliamo eseguire quando il segnale viene ricevuto. Facciamo «lampeggiare» lo sprite:

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;
    }
}

Esegui la scena e vedrai lo Sprite che lampeggia ogni secondo. Puoi cambiare il Tempo di Attesa del Timer nelle proprietà per alterare questa funzionalità.

Collegamento di segnali nel codice

Puoi anche connettere i segnali da codice invece che dall’editor. Questo è di solito necessario quando stai instanziando nodi da codice e quindi non puoi usare l’editor per fare la connessione.

Per prima cosa, sconnetti il segnale selezionando la connessione nella tab «Node» del nodo timer e fai click su sconnetti.

../../_images/signals_disconnect_timer.png

Per creare la connessione da codice, possiamo utilizzare la funzione connect. La inseriremo in _ready() cosi che la connessione venga creata durante l’esecuzione. La sintassi della funzione è <source_node>.connect(<signal_name>, <target_node>, <target_function_name>). Qui c’è il codice per la connessione del nostro 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;
    }
}

Segnali personalizzati

Puoi anche dichiarare i tuoi segnali personalizzati in Godot:

extends Node2D

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

Una volta dichiarati, i tuoi segnali personalizzati appariranno nell’Ispettore e potranno essere connessi nello stesso modo di un segnale pre-costruito.

Per emettere un segnale da codice, usa la funzione 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));
    }
}

A signal can also optionally declare one or more arguments. Specify the argument names between parentheses:

extends Node

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

Nota

The signal arguments show up in the editor’s node dock, and Godot can use them to generate callback functions for you. However, you can still emit any number of arguments when you emit signals. So it’s up to you to emit the correct values.

To pass values, add them as the second argument to the emit_signal function:

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();

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

Conclusione

Tanti dei Nodi di Godot forniscono segnali che puoi usare per rilevare eventi. Per esempio, un Area2D che rappresenta una moneta emette un segnale body_entered quando il corpo del giocatore entra nella sua collision shape, facendoti capire quando il giocatore l’ha raccolta.

Nella prossima sezione, Il tuo primo gioco, creerai un gioco completo includendo l’uso di segnali per connettere differenti componenti di gioco.