Skip to content

核心概念

架构概述

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)
    # 或者依赖自动清理

最佳实践

  • 及时断开:不再需要时断开连接
  • 避免循环引用:注意对象间的引用关系
  • 使用弱引用:在可能的情况下使用弱引用

性能考虑

优化建议

  1. 合理使用通道:避免过多通道
  2. 过滤信号:减少不必要的回调
  3. 批量处理:合并相关信号
  4. 异步处理:耗时操作使用异步

性能指标

  • 信号分发:高性能 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())

调试功能

  • 信号统计:查看信号使用情况
  • 性能分析:监控信号分发性能
  • 连接追踪:调试信号连接问题

基于 MIT 许可发布