Signal
代表 Object 中某个信号的内置类型。
描述
Signal 即信号,是一种内置的 Variant 类型,代表某个 Object 实例上的某个信号。与所有 Variant 一样,信号可以存储在变量中也可以传递给函数。所有连接到该信号的 Callable(以及扩展出的相应对象)都可以监听事件并对事件进行响应,无需相互直接引用。这样代码就更加灵活,便于管理。你可以使用 Object.has_signal() 来检查某个 Object 对象是否有特定名称的信号。
在 GDScript 中,信号可以使用 signal 关键字声明。在 C# 中,你可以对委托使用 [Signal] 特性。
signal attacked
# 还可以额外声明参数。
# 发出信号时必须传入这些参数。
signal item_dropped(item_name, amount)
[Signal]
delegate void AttackedEventHandler();
// 还可以额外声明参数。
// 发出信号时必须传入这些参数。
[Signal]
delegate void ItemDroppedEventHandler(string itemName, int amount);
连接信号是 Godot 中最常见的操作之一,API 为此提供了许多选项,详见下文。下面的代码展示的是推荐的做法。
func _ready():
var button = Button.new()
# 这里的 `button_down` 是 Signal Variant 类型。因此调用的是 Signal.connect() 方法而不是 Object.connect() 方法。
# 对该 API 的概述见下面的讨论。
button.button_down.connect(_on_button_down)
# 假设存在 `Player` 类,定义了 `hit` 信号。
var player = Player.new()
# 我们再次使用 Signal.connect(),还使用了 Callable.bind() 方法,
# 返回的是绑定了参数的新 Callable。
player.hit.connect(_on_player_hit.bind("sword", 100))
func _on_button_down():
print("按下了按钮!")
func _on_player_hit(weapon_type, damage):
print("使用武器 %s 造成了 %d 点伤害。" % [weapon_type, damage])
public override void _Ready()
{
var button = new Button();
// C# 支持将信号以事件的形式传递,因此我们可以这么写:
button.ButtonDown += OnButtonDown;
// 假设存在 `Player` 类,定义了 `hit` 信号。
var player = new Player();
// 需要绑定额外参数时可以使用 lambda。
player.Hit += () => OnPlayerHit("sword", 100);
}
private void OnButtonDown()
{
GD.Print("按下了按钮!");
}
private void OnPlayerHit(string weaponType, int damage)
{
GD.Print($"使用武器 {weaponType} 造成了 {damage} 点伤害。");
}
``Object.connect()`` 还是 ``Signal.connect()``?
如上所示,并不推荐使用 Object.connect() 来连接信号。下面的代码中展示了连接信号的四种方法,包括这种传统的方法、推荐的 connect() 方法、使用隐式 Callable、以及手动定义。
func _ready():
var button = Button.new()
# 方法 1:Object.connect() 与方法的隐式 Callable。
button.connect("button_down", _on_button_down)
# 方法 2:Object.connect() 与使用目标对象和方法名构造的 Callable。
button.connect("button_down", Callable(self, "_on_button_down"))
# 方法 3:Signal.connect() 与方法的隐式 Callable。
button.button_down.connect(_on_button_down)
# 方法 4:Signal.connect() 与使用目标对象和方法名构造的 Callable。
button.button_down.connect(Callable(self, "_on_button_down"))
func _on_button_down():
print("Button down!")
public override void _Ready()
{
var button = new Button();
// 方法 1:在 C# 中,我们可以将信号以事件的形式使用,因此我们可以这么连接:
button.ButtonDown += OnButtonDown;
// 方法 2:GodotObject.Connect() 与方法组构造的 Callable。
button.Connect(Button.SignalName.ButtonDown, Callable.From(OnButtonDown));
// 方法 3:GodotObject.Connect() 与使用目标对象和方法名构造的 Callable。
button.Connect(Button.SignalName.ButtonDown, new Callable(this, MethodName.OnButtonDown));
}
private void OnButtonDown()
{
GD.Print("按下了按钮!");
}
所有方法的效果都是一样的(button 的 BaseButton.button_down 信号连接到 _on_button_down),方法 3 的校验最为完善:如果 button_down Signal 或 _on_button_down Callable 未定义就会输出编译错误。而方法 2 仅依赖字符串名称,只能在运行时校验名称:如果 "button_down" 不是信号或者 "_on_button_down" 不是 self 对象的方法,就会在运行时生成错误。使用方法 1、2、4 的主要原因是确实需要使用字符串(例如根据从配置文件读取的字符串通过程序来连接信号)。否则建议使用方法 3(而且速度最快)。
参数的绑定和传递:
绑定参数的语法需要使用 Callable.bind(),返回的是当前 Callable 绑定参数后的副本。
调用 emit() 或 Object.emit_signal() 时也可以传递信号的参数。下面的例子展示的是信号参数和绑定参数之间的关系。
func _ready():
# 假设存在 `Player` 类,定义了 `hit` 信号。
var player = Player.new()
# 使用 Callable.bind()。
player.hit.connect(_on_player_hit.bind("sword", 100))
# 发出信号时添加的参数先传递。
player.hit.emit("Dark lord", 5)
# 发出信号时传入了 (`hit_by`, `level`) 两个参数,
# 连接信号时绑定了 (`weapon_type`, `damage`) 两个参数。
func _on_player_hit(hit_by, level, weapon_type, damage):
print("受到来自 %s(%d 级)的攻击,使用武器 %s 造成了 %d 点伤害。" % [hit_by, level, weapon_type, damage])
public override void _Ready()
{
// 假设存在 `Player` 类,定义了 `hit` 信号。
var player = new Player();
// 使用 lambda 表达式创建闭包,捕获额外参数。
// Lambda 只会接受信号委托定义的参数。
player.Hit += (hitBy, level) => OnPlayerHit(hitBy, level, "sword", 100);
// 发出信号时添加的参数先传递。
player.EmitSignal(SignalName.Hit, "Dark lord", 5);
}
// 发出信号时传入了 (`hit_by`, `level`) 两个参数,
// 连接信号时绑定了 (`weapon_type`, `damage`) 两个参数。
private void OnPlayerHit(string hitBy, int level, string weaponType, int damage)
{
GD.Print($"受到来自 {hitBy}({level} 级)的攻击,使用武器 {weaponType} 造成了 {damage} 点伤害。");
}
备注
通过 C# 使用该 API 时会有显著不同,详见 C# API 与 GDScript 的差异。
教程
构造函数
Signal() |
|
Signal(object: Object, signal: StringName) |
方法
void |
disconnect(callable: Callable) |
void |
emit(...) vararg const |
get_connections() const |
|
get_name() const |
|
get_object() const |
|
get_object_id() const |
|
has_connections() const |
|
is_connected(callable: Callable) const |
|
is_null() const |
运算符
operator !=(right: Signal) |
|
operator ==(right: Signal) |
构造函数说明
构造空的 Signal,没有绑定对象和信号名称。
构造给定 Signal 的副本。
Signal Signal(object: Object, signal: StringName)
新建 Signal 对象,引用 object 对象中名为 signal 的信号。
方法说明
int connect(callable: Callable, flags: int = 0) 🔗
将信号连接到可调用体 callable。还可以添加 flags 对连接的行为进行配置(见 ConnectFlags 常量)。可以使用 Callable.bind() 为连接的 callable 提供额外的参数。
一个信号只能和同一个 Callable 连接一次。如果信号已连接,则会返回 @GlobalScope.ERR_INVALID_PARAMETER 并生成错误,除非信号是用 Object.CONNECT_REFERENCE_COUNTED 连接的。要防止这个问题,请先使用 is_connected() 检查已有连接。
for button in $Buttons.get_children():
button.pressed.connect(_on_pressed.bind(button))
func _on_pressed(button):
print(button.name, " 被按了一下")
注意:如果 callable 的对象被释放,连接会丢失。
void disconnect(callable: Callable) 🔗
将该信号与给定的 Callable 断开连接。如果该连接不存在,则会生成错误。请使用 is_connected() 来确保连接存在。
void emit(...) vararg const 🔗
发出该信号。与该信号相连的所有 Callable 都将被触发。此方法支持可变数量的参数,所以参数可以用逗号分隔列表的形式传递。
Array get_connections() const 🔗
返回该信号的连接 Array。连接用 Dictionary 表示,包含三个条目:
signal是对此信号的引用。callable是对连接的 Callable 的引用。flags是 ConnectFlags 的组合。
StringName get_name() const 🔗
返回该信号的名称。
返回发出该信号的对象。
返回发出该信号的对象的 ID(见 Object.get_instance_id())。
bool has_connections() const 🔗
如果存在连接到该信号的 Callable,则返回 true。
bool is_connected(callable: Callable) const 🔗
如果指定的 Callable 已连接到此信号,则返回 true。
如果该 Signal 不存在对象且信号名为空,则返回 true。等价于 signal == Signal()。
运算符说明
bool operator !=(right: Signal) 🔗
如果信号的对象或名称不同,则返回 true。
bool operator ==(right: Signal) 🔗
如果信号的对象和名称相同,则返回 true。