forked from datawhale/whale-town-front
docs: 重新组织文档结构,按开发阶段分类
新的目录结构: 01-项目入门/ # 新人必读,项目基础 02-开发规范/ # 编码标准和规范 03-技术实现/ # 具体开发指导 04-高级开发/ # 进阶开发技巧 05-部署运维/ # 发布和部署 06-功能模块/ # 特定功能文档 新增导航文档: - docs/README.md - 完整的文档导航和使用指南 - 各目录下的README.md - 分类说明和使用指导 优化效果: - 开发者可以按阶段快速定位需要的文档 - 新人有清晰的学习路径 - 不同角色有针对性的文档推荐 - 提供了问题导向的快速查找功能
This commit is contained in:
272
docs/02-开发规范/架构与通信规范.md
Normal file
272
docs/02-开发规范/架构与通信规范.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# 架构与通信规范
|
||||
|
||||
本文档定义了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`
|
||||
- 职责: 全局事件通信
|
||||
|
||||
### 单例使用规范
|
||||
```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)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**记住:良好的架构设计是项目成功的基石!遵循这些通信规范可以确保代码的可维护性和扩展性。**
|
||||
Reference in New Issue
Block a user