Instanciation avec des signaux

Les signaux fournissent un moyen de découpler les objets du jeu, ce qui vous permet d'éviter de forcer un arrangement fixe de nœuds. Un signe qu'un signal peut être appelé est lorsque vous vous trouvez en train d'utiliser get_parent(). Se référer directement au parent d'un noeud signifie que vous ne pouvez pas facilement déplacer ce noeud à un autre endroit de l'arbre de scènes. Cela peut être particulièrement problématique lorsque vous instanciez des objets au moment de l'exécution et que vous souhaitez les placer à un endroit arbitraire dans l'arbre de scènes en cours d'exécution.

Nous allons examiner ci-dessous un exemple d'une telle situation : tirer des balles.

Exemple de tir

Imaginez un personnage joueur qui peut tourner et tirer vers la souris. Chaque fois que le bouton de la souris est cliqué, nous créons une instance de la balle à l'emplacement du joueur. Voir Instanciation pour plus de détails.

Nous allons utiliser une Area2D pour la balle, qui se déplace en ligne droite a une vitesse donnée :

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

Cependant, si les balles sont ajoutées en tant qu'enfants du joueur, elles resteront "attachées" au joueur lors de sa rotation :

../../_images/signals_shoot1.gif

Au lieu de cela, nous avons besoin que les balles soient indépendantes du mouvement du joueur - une fois tirées, elles doivent continuer à voyager en ligne droite et le joueur ne peut plus les affecter. Au lieu d'être ajouté à l'arbre de la scène en tant qu'enfant du joueur, il est plus logique d'ajouter la balle en tant qu'enfant de la scène de jeu "principale", qui peut être le parent du joueur ou même plus haut dans l'arbre.

Vous pouvez le faire en ajoutant directement la balle à la scène principale :

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

Cependant, cela conduira à un problème différent. Maintenant, si vous essayez de tester votre scène "Player" de manière indépendante, elle se bloquera lors du tir, car il n’y a pas de nœud parent auquel accéder. Cela rend beaucoup plus difficile le test indépendant du code de votre joueur et signifie également que si vous décidez de modifier la structure des nœuds de votre scène principale, le parent du joueur risque de ne plus être le nœud approprié pour recevoir les balles.

La solution consiste à utiliser un signal pour "émettre" les balles du joueur. Le joueur n'a alors pas besoin de "savoir" ce qu'il advient des balles après cela. Quel que soit le nœud connecté au signal, il peut "recevoir" les balles et prendre les mesures appropriées pour les générer.

Voici le code pour le joueur utilisant des signaux pour émettre la balle :

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

Dans la scène principale, nous connectons ensuite le signal du joueur (celui-ci apparaîtra dans l'onglet "Nœud").

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

Maintenant, les balles conserveront leur propre mouvement indépendamment de la rotation du joueur :

../../_images/signals_shoot2.gif