# 架构与通信规范 本文档定义了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` 中定义: ```gdscript # _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" ``` ### 事件使用方法 #### 发送事件 ```gdscript # 发送简单事件 EventSystem.emit_event(EventNames.PLAYER_MOVED) # 发送带数据的事件 EventSystem.emit_event(EventNames.PLAYER_HEALTH_CHANGED, { "old_health": 80, "new_health": 60, "damage": 20 }) ``` #### 监听事件 ```gdscript 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) ``` #### 断开事件监听 ```gdscript 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` - 职责: 统一响应处理、错误处理、用户反馈 ### 单例使用规范 ```gdscript # ✅ 正确:高层组件可以访问单例 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. 父子通信 ```gdscript # 父节点调用子节点方法(向下调用) 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. 兄弟组件通信 ```gdscript # 通过共同的父节点中转 # 或使用事件系统 func _notify_sibling() -> void: EventSystem.emit_event(EventNames.COMPONENT_MESSAGE, { "sender": self, "message": "Hello sibling!" }) ``` ### 3. 跨场景通信 ```gdscript # 使用事件系统进行跨场景通信 func _change_scene_with_data() -> void: EventSystem.emit_event(EventNames.SCENE_DATA_TRANSFER, { "target_scene": "battle_scene", "player_data": player_data }) ``` ## 🚫 禁止的通信模式 ### 1. 直接节点引用 ```gdscript # ❌ 错误:直接获取其他场景的节点 func _bad_communication() -> void: var other_scene = get_tree().get_first_node_in_group("other_scene") other_scene.do_something() # 强耦合,难以维护 ``` ### 2. 全局变量传递 ```gdscript # ❌ 错误:使用全局变量传递状态 # 在autoload中: var global_player_data = {} # 避免这种做法 ``` ### 3. 循环依赖 ```gdscript # ❌ 错误:A依赖B,B又依赖A # ComponentA.gd var component_b: ComponentB # ComponentB.gd var component_a: ComponentA # 循环依赖 ``` ## 📋 通信最佳实践 ### 1. 事件数据结构 ```gdscript # 使用结构化的事件数据 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. 错误处理 ```gdscript 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. 性能考虑 ```gdscript # 避免在_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 ``` ## 🧪 测试通信系统 ### 单元测试示例 ```gdscript 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) ``` --- **记住:良好的架构设计是项目成功的基石!遵循这些通信规范可以确保代码的可维护性和扩展性。**