Object.connect
2026/4/14大约 7 分钟
最后同步日期:2026-04-16 | Godot 官方原文 — Object.connect
Object.connect
定义
connect 就像给门铃按钮接上电线——你把一个"信号"(比如"按钮被按下了")连接到一个"处理方法"(比如"播放门铃音乐")。当信号被触发时,与之连接的方法就会自动执行。
在 Godot 中,信号(Signal)是节点之间互相通信的主要方式。想象一下餐厅的传菜铃:厨师做好菜后按一下铃(触发信号),服务员听到铃声就来端菜(执行处理方法)。connect 就是"把铃接到服务员耳朵上"这一步。
一句话总结
connect 的作用是"建立一条电线":把某个事件(信号)和某个回应(方法)绑在一起,事件一发生,回应就自动执行。
函数签名
C#
// 方式一:使用 Connect 方法
public Error Connect(StringName signal, Callable callable, uint flags = 0)
// 方式二(推荐):使用 C# 事件语法
// 对于 Godot 内置信号,可以直接用 += 订阅
someNode.SignalName += OnSignalHandler;GDScript
connect(signal: StringName, callable: Callable, flags: int = 0) -> int参数说明
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
| signal | StringName | 是 | 信号的名称,比如 "timeout"、"pressed"、"body_entered" 等 |
| callable | Callable | 是 | 信号触发时要执行的方法,用 Callable.From(方法名) 或直接传方法引用 |
| flags | int / uint | 否 | 连接标志位,控制连接的特殊行为(见下方说明) |
连接标志(ConnectFlags)
| 标志 | 值 | 说明 |
|---|---|---|
ConnectFlags.OneShot / CONNECT_ONE_SHOT | 1 | 一次性连接——信号触发一次后自动断开,像一次性闹钟 |
ConnectFlags.Deferred / CONNECT_DEFERRED | 2 | 延迟连接——信号触发时不立即执行,等到下一帧空闲时才执行 |
ConnectFlags.Persist / CONNECT_PERSIST | 4 | 持久连接——保存场景时这个连接也会被保存(编辑器用得多) |
ConnectFlags.ReferenceCounted / CONNECT_REFERENCE_COUNTED | 8 | 引用计数——连接会持有目标的引用,防止被垃圾回收 |
返回值
| 类型 | 说明 |
|---|---|
| Error (C#) / int (GDScript) | 返回 Error.Ok(值为 0)表示连接成功;返回其他错误码表示失败(比如信号不存在) |
代码示例
基础用法
最简单的用法——把按钮的 pressed 信号连接到一个处理方法:
C#
using Godot;
public partial class MyMenu : Control
{
private Button _startButton;
public override void _Ready()
{
_startButton = GetNode<Button>("StartButton");
// 方式一:使用 C# 事件语法(推荐)
_startButton.Pressed += OnStartButtonPressed;
// 方式二:使用 Connect 方法
// _startButton.Connect(Button.SignalName.Pressed, Callable.From(OnStartButtonPressed));
GD.Print("信号已连接,等待按钮被按下...");
// 运行结果: 信号已连接,等待按钮被按下...
}
private void OnStartButtonPressed()
{
GD.Print("开始游戏!");
// 运行结果(点击按钮后): 开始游戏!
}
}GDScript
extends Control
var _start_button: Button
func _ready() -> void:
_start_button = get_node("StartButton")
# 把 pressed 信号连接到自定义方法
_start_button.pressed.connect(_on_start_button_pressed)
print("信号已连接,等待按钮被按下...")
# 运行结果: 信号已连接,等待按钮被按下...
func _on_start_button_pressed() -> void:
print("开始游戏!")
# 运行结果(点击按钮后): 开始游戏!实际场景
在游戏开发中,Timer(计时器)是最常配合信号使用的节点。下面的例子展示了一个敌人生成系统:Timer 的 timeout 信号每隔一段时间触发一次,每次触发就生成一个敌人。
C#
using Godot;
public partial class EnemySpawner : Node2D
{
[Export] public PackedScene ExEnemyScene;
[Export] public float ExSpawnInterval = 2.0f;
private Timer _spawnTimer;
private int _spawnedCount = 0;
public override void _Ready()
{
// 创建计时器
_spawnTimer = new Timer();
_spawnTimer.WaitTime = ExSpawnInterval;
_spawnTimer.OneShot = false; // 循环触发
AddChild(_spawnTimer);
// 连接 timeout 信号到生成方法
_spawnTimer.Timeout += OnSpawnTimerTimeout;
// 启动计时器
_spawnTimer.Start();
GD.Print("敌人生成器已启动,每 " + ExSpawnInterval + " 秒生成一个敌人");
// 运行结果: 敌人生成器已启动,每 2 秒生成一个敌人
}
private void OnSpawnTimerTimeout()
{
if (ExEnemyScene == null) return;
var enemy = ExEnemyScene.Instantiate<Node2D>();
enemy.Position = new Vector2(
GD.RandRange(-200, 200),
GD.RandRange(-200, 200)
);
AddChild(enemy);
_spawnedCount++;
GD.Print($"已生成第 {_spawnedCount} 个敌人,位置: {enemy.Position}");
// 运行结果(每2秒输出一次):
// 已生成第 1 个敌人,位置: (123, -45)
// 已生成第 2 个敌人,位置: (-67, 189)
// 已生成第 3 个敌人,位置: (200, -12)
}
}GDScript
extends Node2D
@export var ex_enemy_scene: PackedScene
@export var ex_spawn_interval: float = 2.0
var _spawn_timer: Timer
var _spawned_count: int = 0
func _ready() -> void:
# 创建计时器
_spawn_timer = Timer.new()
_spawn_timer.wait_time = ex_spawn_interval
_spawn_timer.one_shot = false # 循环触发
add_child(_spawn_timer)
# 连接 timeout 信号到生成方法
_spawn_timer.timeout.connect(_on_spawn_timer_timeout)
# 启动计时器
_spawn_timer.start()
print("敌人生成器已启动,每 %s 秒生成一个敌人" % ex_spawn_interval)
# 运行结果: 敌人生成器已启动,每 2.0 秒生成一个敌人
func _on_spawn_timer_timeout() -> void:
if ex_enemy_scene == null:
return
var enemy = ex_enemy_scene.instantiate()
enemy.position = Vector2(
randf_range(-200, 200),
randf_range(-200, 200)
)
add_child(enemy)
_spawned_count += 1
print("已生成第 %d 个敌人,位置: %s" % [_spawned_count, enemy.position])
# 运行结果(每2秒输出一次):
# 已生成第 1 个敌人,位置: (123, -45)
# 已生成第 2 个敌人,位置: (-67, 189)
# 已生成第 3 个敌人,位置: (200, -12)进阶用法
使用 ConnectFlags 控制连接行为,以及带参数的信号连接:
C#
using Godot;
public partial class AdvancedSignalDemo : Node
{
private int _eventCount = 0;
public override void _Ready()
{
// ===== 示例 1:一次性连接(OneShot)=====
// 信号触发一次后自动断开,适合"只做一次"的场景
var startButton = GetNode<Button>("StartButton");
startButton.Pressed += OnGameStarted;
// 也可以用 Connect + flags 实现一次性连接:
// startButton.Connect(Button.SignalName.Pressed,
// Callable.From(OnGameStarted),
// (uint)ConnectFlags.OneShot);
// ===== 示例 2:带参数的信号 =====
// Area2D 的 body_entered 信号会传入一个 Node2D 参数
var detectionZone = GetNode<Area2D>("DetectionZone");
detectionZone.BodyEntered += OnBodyEntered;
// ===== 示例 3:延迟连接(Deferred)=====
// 信号触发后不会立刻执行,而是等到当前帧结束后才执行
var timer = GetNode<Timer>("DeferredTimer");
timer.Connect(Timer.SignalName.Timeout,
Callable.From(OnDeferredEvent),
(uint)ConnectFlags.Deferred);
GD.Print("所有信号连接已建立");
// 运行结果: 所有信号连接已建立
}
private void OnGameStarted()
{
_eventCount++;
GD.Print($"游戏开始!(这是第 {_eventCount} 次触发)");
// 如果用了 OneShot,这个方法只会执行一次
// 运行结果: 游戏开始!(这是第 1 次触发)
}
private void OnBodyEntered(Node2D body)
{
// body 参数是进入检测区域的物体
GD.Print($"有物体进入检测区域: {body.Name}");
// 运行结果: 有物体进入检测区域: Player
}
private void OnDeferredEvent()
{
GD.Print("延迟事件在当前帧结束后执行");
// 运行结果: 延迟事件在当前帧结束后执行
}
}GDScript
extends Node
var _event_count: int = 0
func _ready() -> void:
# ===== 示例 1:一次性连接(OneShot)=====
# 信号触发一次后自动断开,适合"只做一次"的场景
var start_button = get_node("StartButton")
start_button.pressed.connect(_on_game_started, CONNECT_ONE_SHOT)
# ===== 示例 2:带参数的信号 =====
# Area2D 的 body_entered 信号会传入一个 Node2D 参数
var detection_zone = get_node("DetectionZone")
detection_zone.body_entered.connect(_on_body_entered)
# ===== 示例 3:延迟连接(Deferred)=====
# 信号触发后不会立刻执行,而是等到当前帧结束后才执行
var timer = get_node("DeferredTimer")
timer.timeout.connect(_on_deferred_event, CONNECT_DEFERRED)
print("所有信号连接已建立")
# 运行结果: 所有信号连接已建立
func _on_game_started() -> void:
_event_count += 1
print("游戏开始!(这是第 %d 次触发)" % _event_count)
# 因为用了 CONNECT_ONE_SHOT,这个方法只会执行一次
# 运行结果: 游戏开始!(这是第 1 次触发)
func _on_body_entered(body: Node2D) -> void:
# body 参数是进入检测区域的物体
print("有物体进入检测区域: %s" % body.name)
# 运行结果: 有物体进入检测区域: Player
func _on_deferred_event() -> void:
print("延迟事件在当前帧结束后执行")
# 运行结果: 延迟事件在当前帧结束后执行注意事项
- C# 推荐使用事件语法:在 C# 中,连接信号有两种方式。推荐使用
+=事件语法(如_button.Pressed += OnPressed),这比Connect()方法更简洁、更符合 C# 的习惯。Connect方法主要用于需要指定flags参数的场景。 - 不要重复连接:同一个信号连接到同一个方法两次,会导致方法被调用两次。连接前最好用
IsConnected检查是否已经连过了,或者确保连接逻辑只执行一次。 - 连接时方法还不存在会报错:如果你把信号连接到一个不存在的方法,运行时会直接报错。确保方法名拼写正确。
- Callable 的写法差异:C# 中直接传方法名即可(如
OnButtonPressed),GDScript 中也是直接传方法名(如_on_button_pressed)。如果需要绑定额外参数,C# 可以用 lambda 表达式,GDScript 可以用bind()方法。 - 对象被释放后连接自动断开:如果信号发送方或接收方被
QueueFree()销毁,与之相关的连接会自动清理,不会造成内存泄漏。 - C# 差异:C# 中方法名用 PascalCase(
Connect),GDScript 中用 snake_case(connect)。信号名称常量在 C# 中通过SignalName静态类访问(如Button.SignalName.Pressed),GDScript 中直接写字符串(如"pressed")。
