Szenen in­s­tan­ti­ie­ren

Signale bieten eine Möglichkeit Spielobjekte zu entkoppeln, was Ihnen erlaubt eine feste Anordnung von Nodes zu vermeiden. Ein Zeichen dafür, dass ein Signal angefordert werden könnte ist, wenn Sie get_parent() verwenden. Wenn Sie direkt auf das übergeordnete Element eines Nodes verweisen, können Sie diesen Node nicht einfach an eine andere Stelle im Szenenbaum verschieben. Dies kann besonders problematisch sein, wenn Sie Objekte zur Laufzeit instanziieren und sie möglicherweise an einer beliebigen Stelle im Baum der laufenden Szene platzieren möchten.

Below we'll consider an example of such a situation: firing bullets.

Beispiel für Geschosse

Als ein weiteres Beispiel für die Signalverwendung betrachten wir einen Spielercharakter, der sich drehen und in Richtung Maus schießen kann. Jedes Mal, wenn die Maustaste gedrückt wird, erstellen wir eine Instanz des Geschosses an der Position des Spielers. Siehe Instanziieren für Details.

We'll use an Area2D for the bullet, which moves in a straight line at a given velocity:

extends Area2D

var velocity = Vector2.ZERO

func _physics_process(delta):
    position += velocity * delta
public class Bullet : Area2D
{
    Vector2 Velocity = new Vector2();

    public override void _PhysicsProcess(float delta)
    {
        Position += Velocity * delta;
    }
}

Wenn die Geschosse jedoch als Unterelement des Spielers hinzugefügt werden, bleiben sie an dem Spieler "gebunden", während er sich dreht:

../../_images/signals_shoot1.gif

Stattdessen müssen die Geschosse unabhängig von der Bewegung des Spielers sein - einmal abgefeuert, sollten sie sich weiterhin in einer geraden Linie bewegen und der Spieler kann sie nicht mehr beeinflussen. Anstatt das Geschoss als Unterelement des Spielers in den Szenenbaum hinzuzufügen, ist es sinnvoller, das Geschoss als Unterelement der "Haupt"-Spielszene hinzuzufügen, die entweder das übergeordnete Objekt des Spielers oder sogar weiter oben im Baum sein kann.

Sie können dies tun, indem man das Geschoss direkt hinzufügt:

var bullet_instance = Bullet.instance()
get_parent().add_child(bullet_instance)
Node bulletInstance = Bullet.Instance();
GetParent().AddChild(bulletInstance);

Dies führt jedoch zu einem anderen Problem. Wenn Sie nun versuchen, Ihre "Spieler"-Szene unabhängig voneinander zu testen, stürzt sie beim Schießen ab, da es keinen übergeordneten Node gibt, auf den Sie zugreifen können. Dies macht es viel schwieriger, den Spieler-Code unabhängig zu testen. Wenn Sie sich entscheiden die Node-Struktur der Hauptszene zu ändern, ist das übergeordnete Element des Spielers möglicherweise nicht mehr der geeignete Node, um das Geschoss zu empfangen.

Die Lösung dafür ist, ein Signal zu verwenden, um die Geschosse vom Spieler "abzufeuern". Der Spieler muss dann nicht "wissen", was danach mit den Geschossen geschieht. Jeder Node, der mit dem Signal verbunden ist, kann die Geschosse "empfangen" und entsprechende Aktionen durchführen, um sie zu erzeugen.

Source Code für die Spielfigur, die unter Verwendung von Events Geschosse abfeuert:

extends Sprite

signal shoot(bullet, direction, location)

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

func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == BUTTON_LEFT and event.pressed:
            emit_signal("shoot", Bullet, rotation, position)

func _process(delta):
    look_at(get_global_mouse_position())
public class Player : Sprite
{
    [Signal]
    delegate void Shoot(PackedScene bullet, Vector2 direction, Vector2 location);

    private PackedScene _bullet = GD.Load<PackedScene>("res://Bullet.tscn");

    public override void _Input(InputEvent event)
    {
        if (input is InputEventMouseButton mouseButton)
        {
            if (mouseButton.ButtonIndex == (int)ButtonList.Left && mouseButton.Pressed)
            {
                EmitSignal(nameof(Shoot), _bullet, Rotation, Position);
            }
        }
    }

    public override _Process(float delta)
    {
        LookAt(GetGlobalMousePosition());
    }
}

In der Main-Szene verbinden wir dann das Spieler-Signal (dieses wird im "Node"-Tab auftauchen).

func _on_Player_shoot(Bullet, direction, location):
    var b = Bullet.instance()
    add_child(b)
    b.rotation = direction
    b.position = location
    b.velocity = b.velocity.rotated(direction)
public void _on_Player_Shoot(PackedScene bullet, Vector2 direction, Vector2 location)
{
    var bulletInstance = (Bullet)bullet.Instance();
    AddChild(bulletInstance);
    bulletInstance.Rotation = direction;
    bulletInstance.Position = location;
    bulletInstance.Velocity = bulletInstance.Velocity.Rotated(direction);
}

Jetzt werden die Geschosse ihre Bewegung, unabhängig von der Drehung des Spielers, beibehalten:

../../_images/signals_shoot2.gif