核心概念
架构概述
GDSignalBus 采用全局单例模式,为 Godot 4 提供高性能的信号总线系统。其核心架构包括:
- 全局信号总线:集中管理所有信号
- 通道系统:信号分组和隔离
- 实例级信号:对象级别的唯一信号
- 异步处理:后台线程支持
- 信号过滤:条件化订阅
- 信号桥接:原生信号连接
全局信号总线
单例模式
GDSignalBus 使用全局单例模式,确保整个游戏中只有一个信号总线实例:
gdscript
# 获取单例
var bus = SignalBus.get_singleton()
# 任何地方都可以访问
bus.emit("global_event", ["data"])
bus.subscribe("global_event", handler)优势
- 解耦:消除节点间的直接依赖
- 集中管理:所有信号在一个地方管理
- 性能优化:C++ 实现的高性能信号分发
- 内存安全:自动清理无效连接
通道系统
通道概念
通道允许将信号分组,避免命名冲突:
gdscript
# 不同通道的同名信号
bus.emit_on_channel("gameplay", "player_action", ["jump"])
bus.emit_on_channel("ui", "player_action", ["menu_open"])
# 监听特定通道
bus.subscribe_on_channel("gameplay", "player_action", _on_gameplay_action)
bus.subscribe_on_channel("ui", "player_action", _on_ui_action)通道隔离
- 命名空间:避免信号名称冲突
- 逻辑分组:相关信号放在同一通道
- 性能优化:减少不必要的信号处理
实例级信号
唯一标识
为使用相同脚本的不同对象实例提供唯一信号:
gdscript
# Player.gd
extends Node
func _ready():
# 为每个实例创建唯一信号
bus.subscribe_instance(self, "health_changed", _on_health_changed)
func take_damage(amount):
health -= amount
# 发送实例级信号
bus.emit_instance(self, "take_damage", [amount])
func _on_health_changed(args):
var amount = args[0]
update_health_bar(health)应用场景
- 多人游戏:每个玩家的独立事件
- 敌人AI:不同敌人的状态变化
- UI组件:相同组件的不同实例
信号过滤
条件订阅
只接收满足特定条件的信号:
gdscript
# 只接收特定类型的物品收集事件
bus.subscribe_filtered("item_collected",
func(args):
var value = args[0]
return value > 10,
_on_valuable_item
)
# 只接收玩家生命值低于30%的事件
bus.subscribe_filtered("health_changed",
func(args):
var health = args[0]
return health < 30,
_on_low_health
)过滤器类型
- 类型过滤:基于参数类型
- 值过滤:基于参数值
- 自定义过滤:复杂的条件逻辑
异步处理
后台线程
耗时操作在后台线程处理:
gdscript
# 异步文件加载
bus.subscribe_async_with_callbacks("load_file",
func(args):
var path = args[0]
# 模拟耗时操作
OS.delay_msec(1000)
return "Loaded data from " + path,
_on_load_complete,
_on_load_error
)
# 发射异步信号
bus.emit_async("load_file", ["save_data.dat"])异步优势
- 非阻塞:不阻塞主线程
- 性能提升:保持流畅的游戏体验
- 资源管理:自动管理线程生命周期
信号桥接
原生信号连接
将 Godot 节点的原生信号连接到 SignalBus:
gdscript
# 桥接按钮信号
bus.bridge_signal($Button, "pressed")
# 转发信号到不同名称
bus.relay_node_signal($Button, "pressed", "ui_button_pressed")
# 订阅桥接后的信号
bus.subscribe("pressed", _on_button_pressed)
bus.subscribe("ui_button_pressed", _on_ui_button_pressed)桥接优势
- UI 解耦:UI 逻辑与游戏逻辑分离
- 集中管理:所有 UI 事件通过 SignalBus 处理
- 灵活配置:可以重命名和重新路由信号
内存管理
自动清理
GDSignalBus 自动管理内存:
gdscript
# 对象被释放时,相关连接自动清理
func _exit_tree():
# 可选:显式清理(推荐)
bus.unsubscribe_all(self)
# 或者依赖自动清理最佳实践
- 及时断开:不再需要时断开连接
- 避免循环引用:注意对象间的引用关系
- 使用弱引用:在可能的情况下使用弱引用
性能考虑
优化建议
- 合理使用通道:避免过多通道
- 过滤信号:减少不必要的回调
- 批量处理:合并相关信号
- 异步处理:耗时操作使用异步
性能指标
- 信号分发:高性能 C++ 实现
- 内存使用:最小化内存占用
- CPU 开销:零成本抽象设计
调试工具
内置调试
gdscript
# 启用调试模式
bus.set_debug_enabled(true)
# 查看信号统计
print("信号列表: ", bus.get_signal_list())
print("频道列表: ", bus.get_channel_list())
print("订阅者数量: ", bus.get_subscriber_count("player_died"))
print("总发射信号数: ", bus.get_total_signals_emitted())调试功能
- 信号统计:查看信号使用情况
- 性能分析:监控信号分发性能
- 连接追踪:调试信号连接问题