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.

Instantiation with signals

Signals provide a way to decouple game objects, allowing you to avoid forcing a fixed arrangement of nodes. One sign that a signal might be called for is when you find yourself using get_parent(). Referring directly to a node's parent means that you can't easily move that node to another location in the scene tree. This can be especially problematic when you are instantiating objects at runtime and may want to place them in an arbitrary location in the running scene tree.

Di seguito esamineremo un esempio di tale situazione: sparare proiettili.

Esempio di Shooting

Consideriamo un personaggio giocante in grado di girare e sparare verso il puntatore del mouse. Ogni volta che si clicca con il mouse, viene creata un'istanza del proiettile nella posizione del giocatore. Consultare Creazione di istanze per maggiori dettagli.

Utilizzeremo un oggetto Area2D per il proiettile, che si muove in linea retta a una determinata velocità:

extends Area2D

var velocity = Vector2.RIGHT

func _physics_process(delta):
    position += velocity * delta

Tuttavia, se i proiettili vengono aggiunti come elementi figli del giocatore, rimarranno "attaccati" al giocatore mentre gira:

../../_images/signals_shoot1.gif

In pratica, abbiamo bisogno che i proiettili siano indipendenti dai movimenti del giocatore: una volta sparati, dovrebbero continuare a viaggiare in linea retta e il giocatore non dovrebbe più influenzarli. Azzinché aggiungerlo all'albero di scene come figlio del giocatore, ha più senso aggiungere il proiettile come figlio della scena di gioco "principale", che potrebbe essere il genitore del giocatore o ancora più in alto nell'albero.

Si potrebbe fare aggiungendo il proiettile direttamente alla scena principale:

var bullet_instance = Bullet.instantiate()
get_parent().add_child(bullet_instance)

Tuttavia, questo porterà a un problema diverso. Ora, provando a testare la scena "Player" in modo indipendente, si bloccherà durante lo sparo, perché non c'è un nodo padre a cui accedere. Questo rende molto più difficile testare il codice del giocatore in modo indipendente, e significa anche che se si decide di modificare la struttura dei nodi della scena principale, il nodo padre del giocatore potrebbe non essere più il nodo appropriato per ricevere i proiettili.

La soluzione consiste nell'utilizzare un segnale per "emettere" i proiettili dal giocatore. Così il giocatore non ha bisogno di "sapere" cosa succede ai proiettili in seguito: qualsiasi nodo connesso al segnale può "ricevere" i proiettili ed effettuare l'azione appropriata per generarli.

Ecco il codice per il giocatore che utilizza i segnali per emettere il proiettile:

extends Sprite2D

signal shoot(bullet, direction, location)

var Bullet = preload("res://bullet.tscn")

func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
            shoot.emit(Bullet, rotation, position)

func _process(delta):
    look_at(get_global_mouse_position())

Nella scena principale, connettiamo poi il segnale del giocatore (apparirà nella scheda "Nodo" dell'Ispettore)

func _on_player_shoot(Bullet, direction, location):
    var spawned_bullet = Bullet.instantiate()
    add_child(spawned_bullet)
    spawned_bullet.rotation = direction
    spawned_bullet.position = location
    spawned_bullet.velocity = spawned_bullet.velocity.rotated(direction)

Ora i proiettili manterranno il loro movimento indipendentemente dalla rotazione del giocatore:

../../_images/signals_shoot2.gif