- 修复README.md中的emoji字符显示问题 - 移除文档质量评级系统 - 添加贡献者致谢部分,创建详细的CONTRIBUTORS.md - 创建核心系统文件EventNames.gd和ProjectPaths.gd - 更新项目配置文件project.godot,添加输入映射 - 完善各模块文档,修正路径引用问题 - 创建文档更新日志CHANGELOG.md - 优化文档结构和导航系统
280 lines
7.8 KiB
Markdown
280 lines
7.8 KiB
Markdown
# 架构与通信规范
|
||
|
||
本文档定义了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)
|
||
```
|
||
|
||
---
|
||
|
||
**记住:良好的架构设计是项目成功的基石!遵循这些通信规范可以确保代码的可维护性和扩展性。** |