有信号的实例

信号提供了一种分离游戏对象的方法,允许您避免强制固定的节点排列。当你发现自己在使用 get_parent() . 直接引用节点的父节点意味着您不能轻易地将该节点移动到场景树中的另一个位置。当您在运行时实例化对象,并且可能希望将它们放置在正在运行的场景树中的任意位置时,这可能特别有问题。

下面我们将考虑这种情况的一个例子:发射子弹。

拍摄示例

考虑一个可以向鼠标旋转和射击的玩家角色。每次点击鼠标按钮,我们都会在玩家所在位置创建一个子弹实例。见 实例 有关详细信息。

我们将使用 Area2D 对于以给定速度直线运动的子弹:

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

但是,如果项目符号是作为播放器的子项添加的,则它们在播放器旋转时将保持“附加”状态:

../../_images/signals_shoot1.gif

相反,我们需要子弹独立于玩家的移动-一旦发射,他们应该继续直线行进,玩家不再影响他们。与其将子弹作为玩家的子级添加到场景树中,不如将子弹作为“主”游戏场景的子级添加,这可能是玩家的父级,甚至可能是更高的树级。

您可以通过将项目符号直接添加到主场景来完成此操作:

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

然而,这将导致另一个问题。现在,如果您尝试独立测试“player”场景,它将在拍摄时崩溃,因为没有父节点可以访问。这使得独立测试播放器代码变得更加困难,也意味着如果决定更改主场景的节点结构,播放器的父节点可能不再是接收项目符号的合适节点。

解决方法是使用一个信号来“发射”玩家的子弹。然后,玩家就不需要“知道”在那之后子弹会发生什么-连接到信号的任何节点都可以“接收”子弹并采取适当的行动来产生它们。

以下是玩家使用信号发射子弹的代码:

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

在主场景中,我们连接玩家的信号(它将出现在“节点”选项卡中)。

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

现在,子弹将保持自己的运动,而不受玩家旋转的影响:

../../_images/signals_shoot2.gif