Files
whale-town-front/docs/02-开发规范/架构与通信规范.md
moyin 0edd1c740b docs: 完善项目文档和README,修复字符显示问题
- 修复README.md中的emoji字符显示问题
- 移除文档质量评级系统
- 添加贡献者致谢部分,创建详细的CONTRIBUTORS.md
- 创建核心系统文件EventNames.gd和ProjectPaths.gd
- 更新项目配置文件project.godot,添加输入映射
- 完善各模块文档,修正路径引用问题
- 创建文档更新日志CHANGELOG.md
- 优化文档结构和导航系统
2025-12-31 18:58:38 +08:00

7.8 KiB
Raw Blame History

架构与通信规范

本文档定义了WhaleTown项目的架构设计原则和组件间通信规范。

🏛️ 架构设计原则

核心原则

  • "信号向上,调用向下" - 父节点调用子节点方法,子节点发出信号通知父节点
  • 高度解耦 - 通过事件系统实现组件间通信,避免直接依赖
  • 分层架构 - 严格的三层架构:框架层、游戏层、界面层
  • 单一职责 - 每个组件只负责一个明确的功能

分层架构详解

┌─────────────────────────────────────┐
│           UI Layer (界面层)           │
│        UI/Windows/, UI/HUD/         │
├─────────────────────────────────────┤
│         Scenes Layer (游戏层)        │
│   Scenes/Maps/, Scenes/Entities/    │
├─────────────────────────────────────┤
│         _Core Layer (框架层)         │
│  _Core/managers/, _Core/systems/    │
└─────────────────────────────────────┘

🔄 事件系统 (EventSystem)

事件系统位置

  • 文件路径: _Core/systems/EventSystem.gd
  • 自动加载: 必须设置为AutoLoad单例
  • 作用: 全局事件总线,实现跨模块通信

事件命名规范

所有事件名称必须在 _Core/EventNames.gd 中定义:

# _Core/EventNames.gd
class_name EventNames

# 玩家相关事件
const PLAYER_MOVED = "player_moved"
const PLAYER_HEALTH_CHANGED = "player_health_changed"
const PLAYER_DIED = "player_died"

# 交互事件
const INTERACT_PRESSED = "interact_pressed"
const NPC_TALKED = "npc_talked"
const ITEM_COLLECTED = "item_collected"

# UI事件
const UI_BUTTON_CLICKED = "ui_button_clicked"
const DIALOG_OPENED = "dialog_opened"
const DIALOG_CLOSED = "dialog_closed"

# 游戏状态事件
const GAME_PAUSED = "game_paused"
const GAME_RESUMED = "game_resumed"
const SCENE_CHANGED = "scene_changed"

事件使用方法

发送事件

# 发送简单事件
EventSystem.emit_event(EventNames.PLAYER_MOVED)

# 发送带数据的事件
EventSystem.emit_event(EventNames.PLAYER_HEALTH_CHANGED, {
    "old_health": 80,
    "new_health": 60,
    "damage": 20
})

监听事件

func _ready() -> void:
    # 连接事件监听
    EventSystem.connect_event(EventNames.PLAYER_DIED, _on_player_died)
    EventSystem.connect_event(EventNames.ITEM_COLLECTED, _on_item_collected)

func _on_player_died(data: Dictionary = {}) -> void:
    print("玩家死亡,游戏结束")
    # 处理玩家死亡逻辑

func _on_item_collected(data: Dictionary) -> void:
    var item_name = data.get("item_name", "未知物品")
    print("收集到物品: ", item_name)

断开事件监听

func _exit_tree() -> void:
    # 节点销毁时断开事件监听
    EventSystem.disconnect_event(EventNames.PLAYER_DIED, _on_player_died)
    EventSystem.disconnect_event(EventNames.ITEM_COLLECTED, _on_item_collected)

🎯 单例管理器

允许的自动加载单例

项目中允许以下五个核心单例:

  1. GameManager - 游戏状态管理

    • 路径: _Core/managers/GameManager.gd
    • 职责: 游戏状态、场景数据、全局配置
  2. SceneManager - 场景管理

    • 路径: _Core/managers/SceneManager.gd
    • 职责: 场景切换、场景生命周期
  3. EventSystem - 事件系统

    • 路径: _Core/systems/EventSystem.gd
    • 职责: 全局事件通信
  4. NetworkManager - 网络管理

    • 路径: _Core/managers/NetworkManager.gd
    • 职责: HTTP请求、API调用、网络状态管理
  5. ResponseHandler - 响应处理

    • 路径: _Core/managers/ResponseHandler.gd
    • 职责: 统一响应处理、错误处理、用户反馈

单例使用规范

# ✅ 正确:高层组件可以访问单例
func _ready() -> void:
    var current_scene = SceneManager.get_current_scene()
    var game_state = GameManager.get_game_state()

# ❌ 错误底层实体不应直接访问GameManager
# 在Player.gd或NPC.gd中避免这样做
func _ready() -> void:
    GameManager.register_player(self)  # 不推荐
    
# ✅ 正确:使用事件系统
func _ready() -> void:
    EventSystem.emit_event(EventNames.PLAYER_SPAWNED, {"player": self})

🔗 组件通信模式

1. 父子通信

# 父节点调用子节点方法(向下调用)
func _on_button_pressed() -> void:
    child_component.activate()
    child_component.set_data(some_data)

# 子节点发出信号通知父节点(向上信号)
# 在子节点中:
signal component_activated(data: Dictionary)
signal component_finished()

func _some_action() -> void:
    component_activated.emit({"status": "active"})

2. 兄弟组件通信

# 通过共同的父节点中转
# 或使用事件系统
func _notify_sibling() -> void:
    EventSystem.emit_event(EventNames.COMPONENT_MESSAGE, {
        "sender": self,
        "message": "Hello sibling!"
    })

3. 跨场景通信

# 使用事件系统进行跨场景通信
func _change_scene_with_data() -> void:
    EventSystem.emit_event(EventNames.SCENE_DATA_TRANSFER, {
        "target_scene": "battle_scene",
        "player_data": player_data
    })

🚫 禁止的通信模式

1. 直接节点引用

# ❌ 错误:直接获取其他场景的节点
func _bad_communication() -> void:
    var other_scene = get_tree().get_first_node_in_group("other_scene")
    other_scene.do_something()  # 强耦合,难以维护

2. 全局变量传递

# ❌ 错误:使用全局变量传递状态
# 在autoload中
var global_player_data = {}  # 避免这种做法

3. 循环依赖

# ❌ 错误A依赖BB又依赖A
# ComponentA.gd
var component_b: ComponentB

# ComponentB.gd  
var component_a: ComponentA  # 循环依赖

📋 通信最佳实践

1. 事件数据结构

# 使用结构化的事件数据
EventSystem.emit_event(EventNames.PLAYER_ATTACK, {
    "attacker": self,
    "target": target_enemy,
    "damage": damage_amount,
    "attack_type": "melee",
    "timestamp": Time.get_time_dict_from_system()
})

2. 错误处理

func _on_event_received(data: Dictionary) -> void:
    # 验证数据完整性
    if not data.has("required_field"):
        push_error("事件数据缺少必需字段: required_field")
        return
    
    # 安全地获取数据
    var value = data.get("optional_field", default_value)

3. 性能考虑

# 避免在_process中频繁发送事件
var last_position: Vector2
func _process(delta: float) -> void:
    if global_position.distance_to(last_position) > 10.0:
        EventSystem.emit_event(EventNames.PLAYER_MOVED, {
            "position": global_position
        })
        last_position = global_position

🧪 测试通信系统

单元测试示例

extends GutTest

func test_event_emission():
    # 监听事件
    watch_signals(EventSystem)
    
    # 发送事件
    EventSystem.emit_event(EventNames.PLAYER_MOVED, {"x": 100, "y": 200})
    
    # 验证事件发送
    assert_signal_emitted(EventSystem, "event_raised")

func test_event_data():
    var received_data: Dictionary
    
    # 连接事件监听
    EventSystem.connect_event(EventNames.TEST_EVENT, func(data): received_data = data)
    
    # 发送测试数据
    var test_data = {"test": "value"}
    EventSystem.emit_event(EventNames.TEST_EVENT, test_data)
    
    # 验证数据传递
    assert_eq(received_data, test_data)

记住:良好的架构设计是项目成功的基石!遵循这些通信规范可以确保代码的可维护性和扩展性。