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.
Checking the stable version of the documentation...
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
using Godot;
public partial class Bullet : Area2D
{
public Vector2 Velocity { get; set; } = Vector2.Right;
public override void _PhysicsProcess(double delta)
{
Position += Velocity * (float)delta;
}
}
Tuttavia, se i proiettili vengono aggiunti come elementi figli del giocatore, rimarranno "attaccati" al giocatore mentre gira:
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)
Node bulletInstance = Bullet.Instantiate();
GetParent().AddChild(bulletInstance);
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())
using Godot;
public partial class Player : Sprite2D
{
[Signal]
public delegate void ShootEventHandler(PackedScene bullet, float direction, Vector2 location);
private PackedScene _bullet = GD.Load<PackedScene>("res://Bullet.tscn");
public override void _Input(InputEvent @event)
{
if (@event is InputEventMouseButton mouseButton)
{
if (mouseButton.ButtonIndex == MouseButton.Left && mouseButton.Pressed)
{
EmitSignal(SignalName.Shoot, _bullet, Rotation, Position);
}
}
}
public override void _Process(double delta)
{
LookAt(GetGlobalMousePosition());
}
}
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)
private void OnPlayerShoot(PackedScene bullet, float direction, Vector2 location)
{
var spawnedBullet = bullet.Instantiate<Bullet>();
AddChild(spawnedBullet);
spawnedBullet.Rotation = direction;
spawnedBullet.Position = location;
spawnedBullet.Velocity = spawnedBullet.Velocity.Rotated(direction);
}
Ora i proiettili manterranno il loro movimento indipendentemente dalla rotazione del giocatore: