Object.disconnect
2026/4/14大约 6 分钟
最后同步日期:2026-04-16 | Godot 官方原文 — Object.disconnect
Object.disconnect
定义
disconnect 就像把门铃的电线剪断——之前用 connect 接好的"信号线"被断开了,之后即使信号再被触发,也不会调用对应的处理方法。
打个比方:你之前给手机设了一个闹钟,现在不需要了,就把它关掉。闹钟(信号)还在,但不会再响给你听了(处理方法不再被调用)。disconnect 就是"取消订阅"这一步。
一句话总结
disconnect 的作用是"剪断电线":把之前用 connect 建立的信号连接断开,让信号触发时不再调用对应的处理方法。
函数签名
C#
// 方式一:使用 Disconnect 方法
public void Disconnect(StringName signal, Callable callable)
// 方式二(推荐):使用 C# 事件语法
// 对于之前用 += 连接的信号,用 -= 断开
someNode.SignalName -= OnSignalHandler;GDScript
disconnect(signal: StringName, callable: Callable) -> void参数说明
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
| signal | StringName | 是 | 要断开的信号名称,必须与 connect 时使用的名称完全一致 |
| callable | Callable | 是 | 要断开的处理方法,必须与 connect 时传入的方法引用完全一致 |
返回值
无返回值(void)。
代码示例
基础用法
最简单的用法——连接一个信号后再断开它:
C#
using Godot;
public partial class SignalDisconnectDemo : Control
{
private Button _button;
public override void _Ready()
{
_button = GetNode<Button>("MyButton");
// 第一步:连接信号
_button.Pressed += OnButtonPressed;
GD.Print("信号已连接");
// 运行结果: 信号已连接
// 第二步:断开信号
_button.Pressed -= OnButtonPressed;
GD.Print("信号已断开,再按按钮也不会有反应了");
// 运行结果: 信号已断开,再按按钮也不会有反应了
}
private void OnButtonPressed()
{
GD.Print("按钮被按下了!");
// 断开后这个方法不会再被调用
}
}GDScript
extends Control
var _button: Button
func _ready() -> void:
_button = get_node("MyButton")
# 第一步:连接信号
_button.pressed.connect(_on_button_pressed)
print("信号已连接")
# 运行结果: 信号已连接
# 第二步:断开信号
_button.pressed.disconnect(_on_button_pressed)
print("信号已断开,再按按钮也不会有反应了")
# 运行结果: 信号已断开,再按按钮也不会有反应了
func _on_button_pressed() -> void:
print("按钮被按下了!")
# 断开后这个方法不会再被调用实际场景
在实际开发中,disconnect 最常用的场景是"状态切换":比如角色死亡后断开所有输入信号、暂停游戏时断开计时器信号、或者切换界面时断开旧界面的信号。
下面的例子展示了一个暂停系统:游戏暂停时断开玩家输入信号,恢复时重新连接。
C#
using Godot;
public partial class PauseManager : Node
{
private bool _isPaused = false;
private Player _player;
private Timer _gameTimer;
public override void _Ready()
{
_player = GetNode<Player>("../Player");
_gameTimer = GetNode<Timer>("GameTimer");
// 连接暂停按钮信号
var pauseButton = GetNode<Button>("HUD/PauseButton");
pauseButton.Pressed += TogglePause;
// 连接游戏计时器
_gameTimer.Timeout += OnGameTimerTimeout;
_gameTimer.Start();
GD.Print("游戏已启动");
// 运行结果: 游戏已启动
}
public void TogglePause()
{
_isPaused = !_isPaused;
if (_isPaused)
{
// 暂停:断开玩家输入和游戏计时器
_player.OnPause();
_gameTimer.Timeout -= OnGameTimerTimeout;
_gameTimer.Paused = true;
GD.Print("游戏已暂停,信号已断开");
// 运行结果: 游戏已暂停,信号已断开
}
else
{
// 恢复:重新连接信号
_player.OnResume();
_gameTimer.Timeout += OnGameTimerTimeout;
_gameTimer.Paused = false;
GD.Print("游戏已恢复,信号已重新连接");
// 运行结果: 游戏已恢复,信号已重新连接
}
}
private void OnGameTimerTimeout()
{
GD.Print("游戏计时器触发");
// 运行结果(仅在非暂停状态下输出): 游戏计时器触发
}
}GDScript
extends Node
var _is_paused: bool = false
var _player: CharacterBody2D
var _game_timer: Timer
func _ready() -> void:
_player = get_node("../Player")
_game_timer = get_node("GameTimer")
# 连接暂停按钮信号
var pause_button = get_node("HUD/PauseButton")
pause_button.pressed.connect(_toggle_pause)
# 连接游戏计时器
_game_timer.timeout.connect(_on_game_timer_timeout)
_game_timer.start()
print("游戏已启动")
# 运行结果: 游戏已启动
func _toggle_pause() -> void:
_is_paused = not _is_paused
if _is_paused:
# 暂停:断开游戏计时器信号
_game_timer.timeout.disconnect(_on_game_timer_timeout)
_game_timer.paused = true
print("游戏已暂停,信号已断开")
# 运行结果: 游戏已暂停,信号已断开
else:
# 恢复:重新连接信号
_game_timer.timeout.connect(_on_game_timer_timeout)
_game_timer.paused = false
print("游戏已恢复,信号已重新连接")
# 运行结果: 游戏已恢复,信号已重新连接
func _on_game_timer_timeout() -> void:
print("游戏计时器触发")
# 运行结果(仅在非暂停状态下输出): 游戏计时器触发进阶用法
断开所有连接、安全断开(先检查再断开),以及处理断开时可能遇到的问题:
C#
using Godot;
public partial class SafeDisconnectDemo : Node
{
private Timer _timer;
private int _tickCount = 0;
public override void _Ready()
{
_timer = GetNode<Timer>("TickTimer");
_timer.Timeout += OnTick;
_timer.Start();
// 5 秒后自动断开(通过一次性计时器)
var autoDisconnectTimer = new Timer();
autoDisconnectTimer.WaitTime = 5.0;
autoDisconnectTimer.OneShot = true;
autoDisconnectTimer.Timeout += () =>
{
SafeDisconnectTimer();
};
AddChild(autoDisconnectTimer);
autoDisconnectTimer.Start();
GD.Print("计时器已启动,5 秒后自动断开");
// 运行结果: 计时器已启动,5 秒后自动断开
}
private void OnTick()
{
_tickCount++;
GD.Print($"Tick #{_tickCount}");
// 运行结果(前5秒内每秒输出):
// Tick #1
// Tick #2
// Tick #3
// Tick #4
// Tick #5
// (第5秒后不再输出,因为信号已断开)
}
/// <summary>
/// 安全断开信号:先检查是否已连接,再断开
/// 避免对未连接的信号调用 disconnect 导致报错
/// </summary>
private void SafeDisconnectTimer()
{
if (IsConnected(_timer, Timer.SignalName.Timeout, Callable.From(OnTick)))
{
_timer.Timeout -= OnTick;
GD.Print("信号已安全断开");
// 运行结果: 信号已安全断开
}
else
{
GD.Print("信号本来就未连接,无需断开");
// 运行结果(如果重复调用): 信号本来就未连接,无需断开
}
}
/// <summary>
/// 辅助方法:检查信号是否已连接
/// </summary>
private bool IsConnected(GodotObject source, StringName signal, Callable callable)
{
return source.IsConnected(signal, callable);
}
}GDScript
extends Node
var _timer: Timer
var _tick_count: int = 0
func _ready() -> void:
_timer = get_node("TickTimer")
_timer.timeout.connect(_on_tick)
_timer.start()
# 5 秒后自动断开
var auto_disconnect_timer = Timer.new()
auto_disconnect_timer.wait_time = 5.0
auto_disconnect_timer.one_shot = true
auto_disconnect_timer.timeout.connect(_safe_disconnect_timer)
add_child(auto_disconnect_timer)
auto_disconnect_timer.start()
print("计时器已启动,5 秒后自动断开")
# 运行结果: 计时器已启动,5 秒后自动断开
func _on_tick() -> void:
_tick_count += 1
print("Tick #%d" % _tick_count)
# 运行结果(前5秒内每秒输出):
# Tick #1
# Tick #2
# Tick #3
# Tick #4
# Tick #5
# (第5秒后不再输出,因为信号已断开)
## 安全断开信号:先检查是否已连接,再断开
## 避免对未连接的信号调用 disconnect 导致报错
func _safe_disconnect_timer() -> void:
if _timer.is_connected("timeout", _on_tick):
_timer.timeout.disconnect(_on_tick)
print("信号已安全断开")
# 运行结果: 信号已安全断开
else:
print("信号本来就未连接,无需断开")
# 运行结果(如果重复调用): 信号本来就未连接,无需断开注意事项
- 必须传入与 connect 完全一致的参数:
disconnect的signal名称和callable方法引用必须与当初connect时传入的完全一致,否则会报错或无法断开。 - 断开未连接的信号会报错:如果你尝试断开一个根本没连接过的信号,Godot 会在控制台输出错误信息。建议在断开前用
IsConnected(C#)或is_connected(GDScript)先检查。 - C# 事件语法与 Connect 方法要配对使用:如果你用
+=连接了信号,就必须用-=来断开,不能用Disconnect()方法。反过来也一样。 - 对象销毁时自动断开:如果信号发送方或接收方被
QueueFree()销毁,所有与之相关的连接会自动清理。所以如果你只是在对象被销毁前断开信号,可以省去手动disconnect的步骤。 - 断开信号不影响其他连接:一个信号可以同时连接到多个方法。断开其中一个连接,不会影响其他连接。
- C# 差异:C# 中方法名用 PascalCase(
Disconnect),GDScript 中用 snake_case(disconnect)。C# 中用SignalName静态类访问信号名常量,GDScript 中直接写字符串。
