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 leggere 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

Per vedere come funzionano i segnali, proviamo ad usare un nodo Timer. Creiamo una nuova scena con un Node2D e due figli: un Timer e un nodo Sprite. Nel pannello della scena, rinomina Node2D con 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.

Se attivi il menu Avanzato, vedrai sul lato destro che puoi associare un numero arbitrario di argomenti di (possibilmente) tipi diversi. Questo può essere utile quando hai più di un segnale collegato allo stesso metodo, poiché ogni propagazione del segnale comporterà valori diversi per quegli argomenti di chiamata extra.

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

Un segnale può anche opzionalmente dichiarare uno o più argomenti. Specifica i nomi degli argomenti tra parentesi:

extends Node


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

Nota

I segnali sono argomenti che vengono visualizzati nel riquadro del nodo dell'editor e Godot può utilizzarli per generare funzioni di callback. Tuttavia, è ancora possibile emettere un numero qualsiasi di argomenti quando si emettono segnali, quindi spetta a te fornire i valori corretti.

Per passare dei valori aggiungili come secondo argomento alla funzione 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);
    }
}

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.