Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

C# 信号

有关信号的详细解释,请参阅逐步教程中的 使用信号 部分。

虽然仍然可以通过 Connect/Disconnect API 使用信号,但 C# 为我们提供了一种更符合习惯的方式来实现 观察者模式

信号作为 C# 事件

为了提供更多的类型安全,Godot 信号也都可以通过 事件 获取。你可以用 +=-= 运算符来处理这些事件,就像其他任何事件一样。

Timer myTimer = GetNode<Timer>("Timer");
myTimer.Timeout += () => GD.Print("Timeout!");

此外,你可以通过节点类型的嵌套 SignalName 类来访问与之相关的信号名称。这在你想要等待一个信号时很有用,例如(参见 await 关键字 )。

await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);

警告

While all engine signals connected as events are automatically disconnected when nodes are freed, custom signals aren't. Meaning that: you will need to manually disconnect (using -=) all the custom signals you connected as C# events (using +=).

自定义信号作为 C# 事件

要在你的 C# 脚本中声明一个自定义事件,你需要在一个公共委托类型上使用 [Signal] 特性。注意,这个委托的名称必须以 EventHandler 结尾。

[Signal]
public delegate void MySignalEventHandler();

[Signal]
public delegate void MySignalWithArgumentEventHandler(string myString);

一旦完成这一步,Godot 就会在后台自动创建相应的事件。你可以像使用任何其他 Godot 信号一样使用这些事件。注意,事件的名称是用你的委托的名称减去最后的 EventHandler 部分来命名的。

public override void _Ready()
{
    MySignal += () => GD.Print("Hello!");
    MySignalWithArgument += SayHelloTo;
}

private void SayHelloTo(string name)
{
    GD.Print($"Hello {name}!");
}

警告

如果你想在编辑器中连接到这些信号,你需要(重新)构建项目以查看它们的出现。

你可以使用右上角的按钮来。

信号发射

要发射信号,使用 EmitSignal 方法。请注意,就像引擎定义的信号一样,你的自定义信号名称列在嵌套的 SignalName 类下。

public void MyMethodEmittingSignals()
{
    EmitSignal(SignalName.MySignal);
    EmitSignal(SignalName.MySignalWithArgument, "World");
}

与其他 C# 事件不同,你不能使用 Invoke 来触发与 Godot 信号绑定的事件。

信号支持任何 Variant 兼容 类型的参数。

Consequently, any Node or RefCounted will be compatible automatically, but custom data objects will need to inherit from GodotObject or one of its subclasses.

using Godot;

public partial class DataObject : GodotObject
{
    public string MyFirstString { get; set; }
    public string MySecondString { get; set; }
}

绑定值

有时你会想在连接建立时将值绑定到信号,而不是(或者除了)在信号发出时。要做到这一点,你可以使用一个匿名函数,如下面的例子所示。

在这里, Button.Pressed 信号不需要任何参数。但我们想要对“加”和“减”按钮使用相同的 ModifyValue 。所以我们在连接信号的时候绑定了修饰值。

public int Value { get; private set; } = 1;

public override void _Ready()
{
    Button plusButton = GetNode<Button>("PlusButton");
    plusButton.Pressed += () => ModifyValue(1);

    Button minusButton = GetNode<Button>("MinusButton");
    minusButton.Pressed += () => ModifyValue(-1);
}

private void ModifyValue(int modifier)
{
    Value += modifier;
}

运行时创建信号

最后,你可以在游戏运行时直接创建自定义信号。使用 AddUserSignal 方法来实现这一功能。注意,这个方法应该在使用这些信号(无论是连接还是发射)之前执行。另外,注意这种方式创建的信号不会通过 SignalName 嵌套类显示。

public override void _Ready()
{
    AddUserSignal("MyCustomSignal");
    EmitSignal("MyCustomSignal");
}