信号桥接
概述
信号桥接功能允许将 Godot 节点的原生信号连接到全局信号总线,实现集中化的事件管理。这对于解耦 UI 逻辑、游戏逻辑和系统逻辑非常有用。
基础桥接示例
完整示例代码
gdscript
@warning_ignore_start("static_called_on_instance")
extends Node
# 原生信号桥接示例
# 演示如何将 Godot 节点的原生信号桥接到 SignalBus
@onready var bus = SignalBus.get_singleton()
var bridge_ids = []
func _ready():
bus.set_debug_enabled(false)
print("\n=== 原生信号桥接示例 ===\n")
bus.bridge_signal($Button, "pressed")
bus.relay_node_signal($Button2, "pressed", "my_pressed")
bus.subscribe("pressed", func():
print("按钮被点击了!(通过 SignalBus)")
)
bus.subscribe("my_pressed", func():
print("重命名桥接按钮被点击了!(通过 SignalBus)")
)桥接方法
1. bridge_signal()
将节点信号直接桥接到 SignalBus,保持原始信号名称。
gdscript
var bridge_id = bus.bridge_signal(node: Node, signal_name: String)参数:
node: 节点对象signal_name: 节点信号名称
返回值:
int64_t: 桥接ID,用于后续管理
示例:
gdscript
# 将按钮的 pressed 信号桥接到 SignalBus
bus.bridge_signal($Button, "pressed")
# 订阅桥接后的信号
bus.subscribe("pressed", func():
print("按钮被点击了!(通过 SignalBus)")
)2. relay_node_signal()
将节点信号转发为不同的 SignalBus 信号名称。
gdscript
var bridge_id = bus.relay_node_signal(node: Node, node_signal: String, bus_signal: String)参数:
node: 节点对象node_signal: 节点信号名称bus_signal: SignalBus 信号名称
返回值:
int64_t: 桥接ID
示例:
gdscript
# 将按钮的 pressed 信号转发为 my_pressed 信号
bus.relay_node_signal($Button2, "pressed", "my_pressed")
# 订阅转发后的信号
bus.subscribe("my_pressed", func():
print("重命名桥接按钮被点击了!(通过 SignalBus)")
)桥接管理
桥接ID存储
gdscript
var bridge_ids = []
func _ready():
var bridge1 = bus.bridge_signal($Button, "pressed")
var bridge2 = bus.relay_node_signal($Button2, "pressed", "my_pressed")
bridge_ids.append_array([bridge1, bridge2])
func _exit_tree():
# 清理所有桥接
for bridge_id in bridge_ids:
bus.unbridge_signal(bridge_id)桥接移除
gdscript
# 移除特定桥接
var success = bus.unbridge_signal(bridge_id)
if success:
print("桥接移除成功")
else:
print("桥接移除失败")实际应用场景
1. UI 事件集中管理
gdscript
# UIManager.gd
extends Node
@onready var bus = SignalBus.get_singleton()
var ui_bridges = []
func _ready():
setup_ui_bridges()
# 订阅所有UI事件
bus.subscribe("menu_opened", _on_menu_opened)
bus.subscribe("settings_changed", _on_settings_changed)
bus.subscribe("game_paused", _on_game_paused)
func setup_ui_bridges():
# 主菜单按钮
var start_bridge = bus.relay_node_signal($StartButton, "pressed", "game_started")
var settings_bridge = bus.relay_node_signal($SettingsButton, "pressed", "settings_opened")
var quit_bridge = bus.relay_node_signal($QuitButton, "pressed", "game_quit")
# 设置界面按钮
var apply_bridge = bus.relay_node_signal($ApplyButton, "pressed", "settings_applied")
var cancel_bridge = bus.relay_node_signal($CancelButton, "pressed", "settings_cancelled")
ui_bridges.append_array([start_bridge, settings_bridge, quit_bridge, apply_bridge, cancel_bridge])
func _on_menu_opened():
print("菜单打开")
show_main_menu()
func _on_settings_changed():
print("设置已更改")
apply_settings()
func _on_game_paused():
print("游戏暂停")
toggle_pause()
func _exit_tree():
for bridge_id in ui_bridges:
bus.unbridge_signal(bridge_id)2. 游戏事件桥接
gdscript
# GameManager.gd
extends Node
@onready var bus = SignalBus.get_singleton()
var game_bridges = []
func _ready():
setup_game_bridges()
# 订阅游戏事件
bus.subscribe("player_health_changed", _on_player_health_changed)
bus.subscribe("enemy_defeated", _on_enemy_defeated)
bus.subscribe("level_completed", _on_level_completed)
func setup_game_bridges():
# 玩家事件
var player_health_bridge = bus.relay_node_signal($Player, "health_changed", "player_health_changed")
var player_died_bridge = bus.relay_node_signal($Player, "died", "player_died")
# 敌人事件(假设敌人有 died 信号)
var enemies = get_tree().get_nodes_in_group("enemies")
for enemy in enemies:
var enemy_bridge = bus.relay_node_signal(enemy, "died", "enemy_defeated")
game_bridges.append(enemy_bridge)
game_bridges.append_array([player_health_bridge, player_died_bridge])
func _on_player_health_changed(health):
print("玩家生命值变化: ", health)
update_health_ui(health)
func _on_enemy_defeated():
print("敌人被击败")
update_score()
func _on_level_completed():
print("关卡完成")
load_next_level()
func _exit_tree():
for bridge_id in game_bridges:
bus.unbridge_signal(bridge_id)3. 系统事件桥接
gdscript
# SystemManager.gd
extends Node
@onready var bus = SignalBus.get_singleton()
var system_bridges = []
func _ready():
setup_system_bridges()
# 订阅系统事件
bus.subscribe("scene_ready", _on_scene_ready)
bus.subscribe("physics_tick", _on_physics_tick)
func setup_system_bridges():
# 场景树事件
var scene_ready_bridge = bus.relay_node_signal(get_tree(), "tree_changed", "scene_ready")
# 物理帧事件
var physics_bridge = bus.relay_node_signal(get_tree(), "physics_frame", "physics_tick")
system_bridges.append_array([scene_ready_bridge, physics_bridge])
func _on_scene_ready():
print("场景准备就绪")
initialize_game()
func _on_physics_tick():
# 处理物理相关的游戏逻辑
update_physics()
func _exit_tree():
for bridge_id in system_bridges:
bus.unbridge_signal(bridge_id)最佳实践
1. 桥接生命周期管理
gdscript
extends Node
@onready var bus = SignalBus.get_singleton()
var bridge_ids = []
func _ready():
# 创建桥接
var bridge_id = bus.bridge_signal($Button, "pressed")
bridge_ids.append(bridge_id)
func _exit_tree():
# 清理桥接
for bridge_id in bridge_ids:
bus.unbridge_signal(bridge_id)
bridge_ids.clear()2. 错误处理
gdscript
func safe_create_bridge(node, signal_name):
if not node:
push_error("节点为空")
return -1
if not node.has_signal(signal_name):
push_error("节点不存在信号: " + signal_name)
return -1
var bridge_id = bus.bridge_signal(node, signal_name)
if bridge_id == -1:
push_error("桥接创建失败")
return -1
return bridge_id3. 调试技巧
gdscript
func _ready():
bus.set_debug_enabled(true)
# 创建桥接时记录信息
var bridge_id = bus.bridge_signal($Button, "pressed")
print("创建桥接: Button.pressed -> ", bridge_id)4. 命名约定
gdscript
# 使用一致的桥接命名
var ui_button_bridge = bus.bridge_signal($StartButton, "pressed")
var gameplay_event_bridge = bus.relay_node_signal($Player, "died", "player_death")5. 性能考虑
gdscript
# 避免频繁创建和销毁桥接
# 对于临时节点,考虑直接使用信号而不是桥接
# 对于大量相似节点,使用批量处理
func setup_enemy_bridges(enemies):
for enemy in enemies:
var bridge_id = bus.relay_node_signal(enemy, "died", "enemy_defeated")
bridge_ids.append(bridge_id)桥接的优势
- 解耦: 将UI逻辑与游戏逻辑分离
- 集中管理: 所有事件通过SignalBus统一处理
- 灵活性: 可以动态添加和移除桥接
- 调试友好: 可以通过SignalBus的调试功能监控所有事件
- 扩展性: 易于添加新的事件处理逻辑
通过信号桥接系统,你可以构建高度解耦、易于维护的游戏架构,将不同层次的逻辑清晰地分离,同时保持系统的整体性和一致性。