信号
简介
信号是Godot的 观察者 模式的版本.它们允许一个节点发出其他节点可以监听和响应的消息.例如,与其持续检查按钮是否被按下,不如在按下按钮时发出信号.
注解
您可以在这里http://gameprogrammingpatterns.com/observer.html阅读有关观察者模式的更多信息
信号是一种使游戏对象 解耦 的方法,从而可以使代码组织得更好,更易于管理.游戏对象可以发出所有感兴趣的对象可以订阅并响应的信号,而非强制游戏对象期望其他对象始终存在.
接下来,您可以看到一些有关如何在自己的项目中使用信号的示例.
计时器示例
为了看看信号是如何工作的,让我们尝试使用一个 Timer 节点.创建一个新的场景,有一个Node2D和两个子节点:一个定时器和一个 Sprite. 在Scene dock中,将Node2D重命名为TimerExample.
对于 Sprite
的纹理,可以使用Godot图标,或您喜欢的任何其他图像.为此,请在 Sprite
的 Texture
属性下拉菜单中选择 Load
.将脚本附加到根节点,但尚未添加任何代码.
您的场景树应该是这样的:
在 Timer
节点的属性中,勾选 自动启动 旁边的选框.这会令计时器在您运行场景时自动启动.您可以将 等待时间 保留为1秒.
在 “属性检查器” 选项卡旁边是一个标记为 “节点” 的选项卡.单击此选项卡,您将看到所选节点可以发出的所有信号.对于 Timer
节点,我们关注的是”timeout()”.每当计时器到达 0
时,就会发出这个信号.
点击”timeout()”信号,然后点击界面底部的”连接…”按钮.您将看到如下窗口,您可以在其中定义如何连接信号:
在左侧,你将看到场景中的节点,并可以选择要”监听”信号的节点.请注意,Timer 节点为蓝色——意思是它是发出信号的节点.选择根节点.
警告
目标节点 必须 附加一个脚本,否则您将收到一条错误消息.
如果你切换高级菜单,你会在右侧看到,你可以绑定任意数量的不同类型的参数.当你有多个信号连接到同一个方法时,这将很有用,因为每个信号的传播,可能会附加不同值的调用参数.
窗口底部是一个标有”Receiver Method”的字段.这是您要使用的目标节点脚本中的函数名称.默认情况下,Godot将使用命名约定 _on_<node_name>_<signal_name>
创建此函数,但如果你想要修改它也可以.
单击 连接(Connect)
,您将看到该函数已在脚本中创建:
GDScript
C#
extends Node2D
func _on_Timer_timeout():
pass # Replace with function body.
public class TimerExample : Node2D
{
public void _on_Timer_timeout()
{
// Replace with function body.
}
}
现在,我们可以用接收信号时要运行的任何代码替换占位符代码.让我们让 Sprite
闪烁一下:
GDScript
C#
extends Node2D
func _on_Timer_timeout():
# Note: the `$` operator is a shorthand for `get_node()`,
# so `$Sprite` is equivalent to `get_node("Sprite")`.
$Sprite.visible = !$Sprite.visible
public class TimerExample : Node2D
{
public void _on_Timer_timeout()
{
var sprite = GetNode<Sprite>("Sprite");
sprite.Visible = !sprite.Visible;
}
}
运行场景,您将看到 Sprite
每秒闪烁一次.您可以更改计时器的 等待时间 属性来更改此设置.
用代码连接信号
您还可以用在代码进行信号连接而不是用编辑器.当您通过代码实例化节点时,通常这是必需的,因为您无法使用编辑器进行连接.
首先,通过在 计时器
的”节点”选项卡中选择连接并点击断开来断开信号.
要使用代码进行连接,我们可以使用 connect
函数.将把它放在 _ready()
函数中,这样连接就会在运行时创建.函数的语法是 <source_node>.connect(<signal_name>, <target_node>, <target_function_name>)
.下面是我们的计时器连接的代码:
GDScript
C#
extends Node2D
func _ready():
$Timer.connect("timeout", self, "_on_Timer_timeout")
func _on_Timer_timeout():
$Sprite.visible = !$Sprite.visible
public class TimerExample : Node2D
{
public override void _Ready()
{
GetNode("Timer").Connect("timeout", this, nameof(_on_Timer_timeout));
}
public void _on_Timer_timeout()
{
var sprite = GetNode<Sprite>("Sprite");
sprite.Visible = !sprite.Visible;
}
}
自定义信号
您还可以在Godot中声明自己的自定义信号:
GDScript
C#
extends Node2D
signal my_signal
public class Main : Node2D
{
[Signal]
public delegate void MySignal();
}
声明后,您的自定义信号将出现在检查器中,并且可以按照与节点的内置信号相同的方式进行连接.
要通过代码发出信号,使用 emit_signal
函数:
GDScript
C#
extends Node2D
signal my_signal
func _ready():
emit_signal("my_signal")
public class Main : Node2D
{
[Signal]
public delegate void MySignal();
public override void _Ready()
{
EmitSignal(nameof(MySignal));
}
}
信号还可以选择声明一个或多个参数.在括号之间指定参数名称:
GDScript
C#
extends Node
signal my_signal(value, other_value)
public class Main : Node
{
[Signal]
public delegate void MySignal(bool value, int other_value);
}
注解
这些信号参数显示在编辑器的节点停靠面板中,Godot可以使用它们为您生成回调函数.但是,发出信号时仍然可以发出任意数量的参数;所以由你来决定是否发出正确的值.
要传递数值,请将数值作为第二个参数添加到 emit_signal
函数中:
GDScript
C#
extends Node
signal my_signal(value, other_value)
func _ready():
emit_signal("my_signal", true, 42)
public class Main : Node
{
[Signal]
public delegate void MySignal(bool value, int other_value);
public override void _Ready()
{
EmitSignal(nameof(MySignal), true, 42);
}
}
总结
Godot的许多内置节点类型提供了可用于检测事件的信号.例如,代表硬币的 Area2D 在游戏角色的物理体进入碰撞形状时发出 body_entered
信号,让您知道游戏角色何时收集了它.
在下一节 您的第一个游戏 中,您将构建一个完整的游戏,其中包括使用多种信号来连接不同的游戏组件.