forked from datawhale/whale-town-front
新增文档: - docs/输入映射配置.md - 游戏输入配置指南 - docs/架构与通信规范.md - 项目架构和组件通信规范 - docs/实现细节规范.md - 游戏对象具体实现要求 - docs/开发哲学与最佳实践.md - 开发理念和编程最佳实践 覆盖内容: - 输入映射的配置方法和验证 - EventSystem事件系统使用规范 - 玩家、NPC、TileMap的实现标准 - 代码质量标准和审查清单 - 性能优化和资源管理指导 这些文档补充了开发规范.md中提到但在docs目录中缺失的内容
7.5 KiB
7.5 KiB
架构与通信规范
本文档定义了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)
🎯 单例管理器
允许的自动加载单例
项目中只允许以下三个单例:
-
GameManager - 游戏状态管理
- 路径:
_Core/managers/GameManager.gd - 职责: 游戏状态、场景数据、全局配置
- 路径:
-
SceneManager - 场景管理
- 路径:
_Core/managers/SceneManager.gd - 职责: 场景切换、场景生命周期
- 路径:
-
EventSystem - 事件系统
- 路径:
_Core/systems/EventSystem.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依赖B,B又依赖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)
记住:良好的架构设计是项目成功的基石!遵循这些通信规范可以确保代码的可维护性和扩展性。