Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Utilizzo dei segnali

In questa lezione, studieremo i segnali. Sono messaggi che i nodi emettono quando accade loro qualcosa di specifico, come la pressione di un pulsante. Altri nodi possono connettersi a quel segnale e richiamare una funzione quando si verifica l'evento.

I segnali sono un meccanismo di delegazione integrato in Godot che consente a un oggetto di gioco di reagire a un cambiamento in un altro senza che i due oggetti si referenziano fra di loro. L'utilizzo dei segnali limita l'accoppiamento e mantiene il codice flessibile.

Ad esempio, potresti avere una barra della vita sullo schermo che rappresenta la salute del giocatore. Quando il giocatore subisce danni o usa una pozione di vita, vuoi che la barra rifletta il cambiamento. Per fare ciò, in Godot, si utilizzano i segnali.

Come i metodi (Callable), i segnali sono un tipo di prima classe a partire da Godot 4.0. Questo significa che è possibile passarli direttamente come argomenti di metodo senza doverli passare come stringhe, il che migliora il completamento automatico ed è meno soggetto a errori. Consulta il riferimento alla classe Signal per una lista di ciò che si può fare direttamente con il tipo Signal.

Vedi anche

Come accennato nell'introduzione, i segnali sono la versione di Godot dell'observer pattern. Puoi saperne di più in Game Programming Patterns.

Ora utilizzeremo un segnale per far muovere e fermare l'icona di Godot della lezione precedente (Ascoltare l'input del giocatore) premendo un pulsante.

Nota

Per questo progetto, seguiremo le convenzioni di denominazione di Godot.

  • GDScript: Le classi (nodi) usano PascalCase, le variabili e le funzioni snake_case e le costanti usano ALL_CAPS (Vedi Guida di stile di GDScript).

  • C#: Le classi, le variabili di esportazione e i metodi usano PascalCase, i campi privati usano _camelCase, le variabili locali e i parametri usano camelCase (Consultare Guida di stile C#). Bisogna fare attenzione a scrivere il nome dei metodi correttamente quando si connettono i segnali.

Impostazione della scena

Per aggiungere un pulsante al nostro gioco, creeremo una nuova scena che includerà sia un Button sia la scena sprite_2d.tscn che abbiamo creato nella lezione Creare il tuo primo script.

Crea una nuova scena andando al menu Scene > New Scene.

../../_images/signals_01_new_scene.webp

Nel pannello Scena, clicca sul pulsante 2D Scene. Questo aggiungerà un Node2D come radice.

../../_images/signals_02_2d_scene.webp

Nel pannello FileSystem, clicca e trascina il file sprite_2d.tscn salvato in precedenza sul Node2D per crearne un'istanza.

../../_images/signals_03_dragging_scene.webp

Vogliamo aggiungere un altro nodo come fratello dello Sprite2D. Per farlo, facciamo clic destro sul Node2D e selezioniamo Add Child Node.

../../_images/signals_04_add_child_node.webp

Cerca il nodo Button e aggiungilo.

../../_images/signals_05_add_button.webp

Il nodo è piccolo normalmente. Clicca e trascina l'handle inferiore destro del pulsante nella viewport per ridimensionarlo.

../../_images/signals_06_drag_button.png

Se non vedi gli handle, assicurati che lo strumento di selezione sia attivo nella barra degli strumenti.

../../_images/signals_07_select_tool.webp

Clicca e trascina il pulsante stesso per spostarlo più vicino allo sprite.

Puoi anche scrivere un'etichetta sul pulsante modificandone la proprietà Text nell'Inspector Inserisci Toggle motion.

../../_images/signals_08_toggle_motion_text.webp

L'albero della scena e la viewport dovrebbero apparire così.

../../_images/signals_09_scene_setup.webp

Salva la scena appena creata come node_2d.tscn, se non l'hai già fatto. Puoi quindi eseguirla con F6 (Cmd + R su macOS). Al momento, il pulsante sarà visibile, ma nulla accadrà se viene premuto.

Collegare segnali nell'editor

Qui, vogliamo collegare il segnale "pressed" del pulsante al nostro Sprite2D e vogliamo chiamare una nuova funzione che ne attivi e disattivi il movimento. Dobbiamo avere uno script collegato al nodo Sprite2D, come abbiamo fatto nella lezione precedente.

È possibile collegare i segnali nel pannello Signals. Seleziona il nodo Button e, sul lato destro dell'editor, clicca sulla scheda chiamata Signals accanto all'Inspector.

../../_images/signals_10_node_dock.webp

Il pannello visualizza un elenco dei segnali disponibili nel nodo selezionato.

../../_images/signals_11_pressed_signals.webp

Fai doppio clic sul segnale "pressed" per aprire la finestra di connessione del nodo.

../../_images/signals_12_node_connection.webp

Da qui, puoi collegare il segnale al nodo Sprite2D. Il nodo ha bisogno di un metodo ricevitore, una funzione che Godot chiamerà quando il pulsante emette il segnale. L'editor ne genera uno automaticamente. Per convenzione, chiamiamo questi metodi di callback "_on_node_name_signal_name". Qui, sarà "_on_button_pressed".

Nota

Quando si collegano i segnali tramite il pannello Segnali dell'editor, è possibile utilizzare due modalità. Quella semplice consente solo di connettersi ai nodi a cui è allegato uno script e crea una nuova funzione di callback su di essi.

../../_images/signals_advanced_connection_window.webp

La vista avanzata consente di connettersi a qualsiasi nodo e a qualsiasi funzione integrata, aggiungere argomenti al callback e impostare certe opzioni. È possibile attivare la modalità cliccando sul pulsante Advanced in basso a destra alla finestra.

Nota

Se si utilizza un editor esterno (come VS Code), la generazione automatica del codice potrebbe non funzionare. In tal caso, è necessario collegare il segnale tramite codice, come spiegato nella sezione successiva.

Clicca sul pulsante Connect per completare la connessione del segnale e passare allo spazio di lavoro Script. Dovresti vedere il nuovo metodo con un'icona di connessione sul margine sinistro.

../../_images/signals_13_signals_connection_icon.webp

Cliccando sull'icona, si apre una finestra con le informazioni sulla connessione. Questa funzione è disponibile solo quando si collegano nodi nell'editor.

../../_images/signals_14_signals_connection_info.webp

Sostituiamo la riga con la parola chiave pass con il codice che attiverà e disattiverà il movimento del nodo.

Il nostro Sprite2D si muove grazie al codice nella funzione _process(). Godot fornisce un metodo per attivare o disattivare l'elaborazione: Node.set_process(). Un altro metodo della classe Node, is_processing(), restituisce true se l'elaborazione "idle" è attiva. Possiamo usare la parola chiave not per invertire il valore.

func _on_button_pressed():
    set_process(not is_processing())

Questa funzione alterna l'elaborazione e, di conseguenza, il movimento dell'icona quando il pulsante viene premuto.

Prima di provare il gioco, dobbiamo semplificare la nostra funzione _process() per muovere il nodo automaticamente, senza attendere l'input dell'utente. Sostituiscila con il seguente codice, che abbiamo visto due lezioni fa:

func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta

Il tuo codice completo sprite_2d.gd dovrebbe essere simile al seguente.

extends Sprite2D

var speed = 400
var angular_speed = PI


func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta


func _on_button_pressed():
    set_process(not is_processing())

Esegui la scena attuale premendo F6 (Cmd + R su macOS) e clicca sul pulsante per vedere lo sprite partire e fermarsi.

Collegare un segnale tramite codice

È possibile collegare i segnali tramite codice anziché tramite l'editor. Questo è necessario quando si creano nodi o si istanziano scene all'interno di uno script.

Usiamo un nodo diverso qui. Godot ha un nodo Timer utile per implementare i tempi di recupero delle abilità, la ricarica delle armi e altro.

Torna allo spazio di lavoro 2D. Puoi cliccare sul testo "2D" nella parte superiore della finestra o premere Ctrl + F1 (Ctrl + Cmd + 1 su macOS).

Nel pannello Scena, fai clic destro sul nodo Sprite2D e aggiungi un nuovo nodo figlio. Cerca Timer e aggiungi il nodo corrispondente. La scena dovrebbe ora apparire così.

../../_images/signals_15_scene_tree.webp

Con il nodo Timer selezionato, vai all'Inspector e abilita la proprietà Autostart.

../../_images/signals_18_timer_autostart.webp

Clicca sull'icona dello script accanto a Sprite2D per tornare allo spazio di lavoro Script.

../../_images/signals_16_click_script.webp

Dobbiamo effettuare due operazioni per connettere i nodi tramite codice:

  1. Ottenere un riferimento al Timer da Sprite2D.

  2. Chiamare il metodo connect() sul segnale "timeout" del Timer.

Nota

Per connettersi a un segnale tramite codice, è necessario chiamare il metodo connect() del segnale che si desidera ascoltare. In questo caso, vogliamo ascoltare il segnale di "timeout" del Timer.

Vogliamo connettere il segnale quando la scena viene istanziata, possiamo farlo attraverso la funzione integrata Node._ready(), che viene chiamata automaticamente dal motore quando un nodo è completamente istanziato.

Per ottenere un riferimento a un nodo relativo a quello attuale, utilizziamo il metodo Node.get_node(). Possiamo memorizzare il riferimento in una variabile.

func _ready():
    var timer = get_node("Timer")

La funzione get_node() esamina i nodi figli dello Sprite2D e li ottiene per il loro nome. Ad esempio, se nell'editor hai rinominato il nodo Timer in "BlinkingTimer", dovresti modificare la chiamata in get_node("BlinkingTimer").

Ora possiamo collegare il Timer allo Sprite2D nella funzione _ready().

func _ready():
    var timer = get_node("Timer")
    timer.timeout.connect(_on_timer_timeout)

La riga è interpreta così: colleghiamo il segnale "timeout" del Timer al nodo a cui è collegato lo script. Quando il Timer emette timeout, vogliamo chiamare la funzione _on_timer_timeout(), che dobbiamo definire. Aggiungiamola in fondo al nostro script e usiamola per alternare la visibilità del nostro sprite.

Nota

Per convenzione, in GDScript chiamiamo questi metodi di callback "_on_node_name_signal_name" e in C# "OnNodeNameSignalName". In questo caso, saranno "_on_timer_timeout" per GDScript e OnTimerTimeout() per C#.

func _on_timer_timeout():
    visible = not visible

La proprietà visible è un valore booleano che controlla la visibilità del nostro nodo. La riga visible = not visible ne inverte il valore. Se visible è true, diventa false, e viceversa.

Se ora esegui la scena Node2D, vedrai che lo sprite si accende e spegne, a intervalli di un secondo.

Script completo

Questo è tutto per la nostra iconcina di Godot che si muove e lampeggia! Ecco il file sprite_2d.gd completo come riferimento.

extends Sprite2D

var speed = 400
var angular_speed = PI


func _ready():
    var timer = get_node("Timer")
    timer.timeout.connect(_on_timer_timeout)


func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta


func _on_button_pressed():
    set_process(not is_processing())


func _on_timer_timeout():
    visible = not visible

Segnali personalizzati

Nota

Questa sezione è un riferimento su come definire e utilizzare i propri segnali e non continua dal progetto creato nelle lezioni precedenti.

È possibile definire segnali personalizzati in uno script. Supponiamo, ad esempio, che tu voglia visualizzare una schermata di fine partita quando la salute del giocatore raggiunge zero. Per farlo, potresti definire un segnale chiamato "died" o "health_depleted" quando la salute del giocatore raggiunge zero.

extends Node2D

signal health_depleted

var health = 10

Nota

Poiché i segnali rappresentano eventi appena accaduti, in genere nei loro nomi utilizziamo un verbo d'azione al passato (past tense).

Questi segnali funzionano allo stesso modo dei segnali integrati: compaiono nella scheda Signals e ci si può connettere a essi come a qualsiasi altro.

../../_images/signals_17_custom_signal.webp

Per emettere un segnale nei tuoi script, chiama emit() sul segnale.

func take_damage(amount):
    health -= amount
    if health <= 0:
        health_depleted.emit()

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

extends Node2D

signal health_changed(old_value, new_value)

var health = 10

Nota

Gli argomenti del segnale appaiono nel pannello Segnali dell'editor, e Godot li può utilizzare per generare funzioni di callback. Tuttavia, è comunque possibile emettere un numero qualsiasi di argomenti quando si emettono segnali, quindi spetta a te fornire i valori corretti.

Per emettere i valori assieme al segnale, aggiungili come secondo argomento alla funzione emit():

func take_damage(amount):
    var old_health = health
    health -= amount
    health_changed.emit(old_health, health)

Riepilogo

Ogni nodo in Godot emette segnali quando vi accade qualcosa di specifico, come la pressione di un pulsante. Altri nodi possono connettersi a singoli segnali e reagire a eventi selezionati.

I segnali hanno molti usi. Con essi, puoi reagire a un nodo che entra o esce dal mondo di gioco, a una collisione, a un personaggio che entra o esce da un'area, a un elemento dell'interfaccia che cambia dimensione e molto altro.

Pesempio, un Area2D che rappresenta una moneta emette un segnale body_entered quando il corpo del giocatore entra nella sua forma di collisione, facendoti sapere quando il giocatore l'ha raccolta.

Nella prossima sezione, Il tuo primo gioco 2D, creerai un gioco 2D completo e metterai in pratica tutto ciò che hai imparato finora.