Ilmentymien luonti signaaleilla

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 instancing objects at runtime and may want to place them in an arbitrary location in the running scene tree.

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

Ammuskeluesimerkki

Consider a player character that can rotate and shoot towards the mouse. Every time the mouse button is clicked, we create an instance of the bullet at the player’s location. See Ilmentymien luonti for 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;
    }
}

Jos lisäisimme luodit pelaajan alisolmuiksi, pysyisivät ne hahmossa ”kiinni” sen pyöriessä:

../../_images/signals_shoot1.gif

Luodin pitäisikin olla pelihahmon liikkeistä riippumattomia – kun luoteja ammutaan, tulisi niiden kulkea suorassa linjassa ja pelaajan ei pitäisi enää vaikuttaa niihin. Sen sijaan, että luodit lisätään skenepuussa pelaajan alisolmuksi, olisi järkevämpää lisätä ne alisolmuiksi ”pääskeneen”, joka saattaa olla pelaajan isäntä tai jopa korkeammalla skenepuussa.

You could do this by adding the bullet to the main scene directly:

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

However, this will lead to a different problem. Now if you try to test your ”Player” scene independently, it will crash on shooting, because there is no parent node to access. This makes it a lot harder to test your player code independently and also means that if you decide to change your main scene’s node structure, the player’s parent may no longer be the appropriate node to receive the bullets.

Ratkaisu ongelmaan löytyy käyttämällä signaalia, joka ”lähettää” luodit pelaajasta. Pelaajan ei tarvitse enää tietää mitä luodeille tämän jälkeen tapahtuu – Se solmu, joka signaaliin on kiinnitetty, voi ottaa luodit ”vastuulleen” ja suorittaa tarvittavat toimenpiteet niiden luomiseksi.

Tässä pelihahmon koodi, jolla luoteja voidaan lähettää signaaleilla:

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

Pelihahmon signaali yhdistetään seuraavaksi pääskenessä (signaali löytyy ”Node”-välilehdestä).

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

Nyt luotien oma liike säilyy riippumattomana pelihahmon kääntymisestä:

../../_images/signals_shoot2.gif