forked from datawhale/whale-town-front
- 重构README文档,参考后端结构优化内容组织 - 添加模块开发指南,详细说明模块化开发流程 - 创建场景设计规范,规范场景架构和最佳实践 - 建立贡献者名单,记录团队成员和贡献统计 - 完善技术栈介绍和功能特性说明 - 优化文档结构和导航,提升开发者体验
464 lines
11 KiB
Markdown
464 lines
11 KiB
Markdown
# 模块开发指南
|
||
|
||
本文档详细说明如何在 Whale Town 项目中开发新的游戏模块。
|
||
|
||
## 🎯 模块设计原则
|
||
|
||
### 核心原则
|
||
1. **单一职责** - 每个模块只负责一个特定的游戏功能
|
||
2. **高内聚低耦合** - 模块内部紧密相关,模块间松散耦合
|
||
3. **可复用性** - 模块应该能在不同场景中复用
|
||
4. **标准化接口** - 统一的模块接口和通信方式
|
||
5. **测试友好** - 模块应该易于测试和调试
|
||
|
||
### 模块分类
|
||
- **UI模块** - 用户界面组件和交互逻辑
|
||
- **游戏逻辑模块** - 角色、战斗、对话等核心功能
|
||
- **系统模块** - 存档、设置、网络等系统功能
|
||
- **工具模块** - 通用工具和辅助功能
|
||
|
||
## 🏗️ 模块结构
|
||
|
||
### 标准目录结构
|
||
```
|
||
module/YourModule/
|
||
├── YourModule.gd # 主模块脚本
|
||
├── components/ # 子组件
|
||
│ ├── Component1.gd
|
||
│ └── Component2.gd
|
||
├── data/ # 模块数据
|
||
│ ├── module_config.json
|
||
│ └── default_data.json
|
||
├── scenes/ # 模块场景
|
||
│ ├── YourModule.tscn
|
||
│ └── components/
|
||
├── tests/ # 模块测试
|
||
│ ├── test_your_module.gd
|
||
│ └── test_components.gd
|
||
└── README.md # 模块文档
|
||
```
|
||
|
||
### 文件命名规范
|
||
- **主模块脚本**: `ModuleName.gd` (PascalCase)
|
||
- **组件脚本**: `ComponentName.gd` (PascalCase)
|
||
- **场景文件**: `module_name.tscn` (snake_case)
|
||
- **数据文件**: `snake_case.json`
|
||
- **测试文件**: `test_module_name.gd`
|
||
|
||
## 📝 模块开发步骤
|
||
|
||
### 第一步:创建模块结构
|
||
|
||
```bash
|
||
# 创建模块目录
|
||
mkdir module/YourModule
|
||
cd module/YourModule
|
||
|
||
# 创建子目录
|
||
mkdir components data scenes tests
|
||
|
||
# 创建主要文件
|
||
touch YourModule.gd
|
||
touch README.md
|
||
touch data/module_config.json
|
||
```
|
||
|
||
### 第二步:实现模块接口
|
||
|
||
每个模块都应该实现标准的模块接口:
|
||
|
||
```gdscript
|
||
# YourModule.gd
|
||
extends Node
|
||
class_name YourModule
|
||
|
||
# 模块信息
|
||
const MODULE_NAME = "YourModule"
|
||
const MODULE_VERSION = "1.0.0"
|
||
const MODULE_AUTHOR = "Your Name"
|
||
|
||
# 模块状态
|
||
enum ModuleState {
|
||
UNINITIALIZED,
|
||
INITIALIZING,
|
||
READY,
|
||
ERROR
|
||
}
|
||
|
||
var current_state: ModuleState = ModuleState.UNINITIALIZED
|
||
var config: Dictionary = {}
|
||
var is_enabled: bool = true
|
||
|
||
# 必需接口方法
|
||
func initialize() -> bool:
|
||
"""初始化模块"""
|
||
current_state = ModuleState.INITIALIZING
|
||
|
||
# 加载配置
|
||
if not _load_config():
|
||
current_state = ModuleState.ERROR
|
||
return false
|
||
|
||
# 初始化组件
|
||
if not _initialize_components():
|
||
current_state = ModuleState.ERROR
|
||
return false
|
||
|
||
# 注册事件监听
|
||
_register_events()
|
||
|
||
current_state = ModuleState.READY
|
||
return true
|
||
|
||
func cleanup():
|
||
"""清理模块资源"""
|
||
_unregister_events()
|
||
_cleanup_components()
|
||
current_state = ModuleState.UNINITIALIZED
|
||
|
||
func get_module_info() -> Dictionary:
|
||
"""获取模块信息"""
|
||
return {
|
||
"name": MODULE_NAME,
|
||
"version": MODULE_VERSION,
|
||
"author": MODULE_AUTHOR,
|
||
"state": current_state,
|
||
"enabled": is_enabled
|
||
}
|
||
|
||
# 私有方法
|
||
func _load_config() -> bool:
|
||
"""加载模块配置"""
|
||
var config_path = "res://module/%s/data/module_config.json" % MODULE_NAME
|
||
if not FileAccess.file_exists(config_path):
|
||
print("警告: 模块配置文件不存在: ", config_path)
|
||
return true # 使用默认配置
|
||
|
||
var file = FileAccess.open(config_path, FileAccess.READ)
|
||
if file == null:
|
||
print("错误: 无法读取配置文件: ", config_path)
|
||
return false
|
||
|
||
var json_string = file.get_as_text()
|
||
file.close()
|
||
|
||
var json = JSON.new()
|
||
var parse_result = json.parse(json_string)
|
||
if parse_result != OK:
|
||
print("错误: 配置文件JSON格式错误: ", config_path)
|
||
return false
|
||
|
||
config = json.data
|
||
return true
|
||
|
||
func _initialize_components() -> bool:
|
||
"""初始化子组件"""
|
||
# 在这里初始化模块的子组件
|
||
return true
|
||
|
||
func _cleanup_components():
|
||
"""清理子组件"""
|
||
# 在这里清理模块的子组件
|
||
pass
|
||
|
||
func _register_events():
|
||
"""注册事件监听"""
|
||
# 使用EventSystem注册需要监听的事件
|
||
# EventSystem.connect_event("event_name", _on_event_handler)
|
||
pass
|
||
|
||
func _unregister_events():
|
||
"""取消事件监听"""
|
||
# 取消所有事件监听
|
||
# EventSystem.disconnect_event("event_name", _on_event_handler)
|
||
pass
|
||
```
|
||
|
||
### 第三步:创建模块配置
|
||
|
||
```json
|
||
// data/module_config.json
|
||
{
|
||
"module_name": "YourModule",
|
||
"version": "1.0.0",
|
||
"enabled": true,
|
||
"dependencies": [],
|
||
"settings": {
|
||
"auto_initialize": true,
|
||
"debug_mode": false
|
||
},
|
||
"resources": {
|
||
"textures": [],
|
||
"sounds": [],
|
||
"data_files": []
|
||
}
|
||
}
|
||
```
|
||
|
||
### 第四步:实现具体功能
|
||
|
||
根据模块类型实现具体功能:
|
||
|
||
#### UI模块示例
|
||
```gdscript
|
||
# UI模块应该继承Control或其子类
|
||
extends Control
|
||
class_name UIModule
|
||
|
||
signal ui_event_triggered(event_name: String, data: Dictionary)
|
||
|
||
func show_ui():
|
||
"""显示UI"""
|
||
visible = true
|
||
# 播放显示动画
|
||
_play_show_animation()
|
||
|
||
func hide_ui():
|
||
"""隐藏UI"""
|
||
# 播放隐藏动画
|
||
_play_hide_animation()
|
||
await get_tree().create_timer(0.3).timeout
|
||
visible = false
|
||
|
||
func _play_show_animation():
|
||
"""播放显示动画"""
|
||
var tween = create_tween()
|
||
modulate.a = 0.0
|
||
tween.tween_property(self, "modulate:a", 1.0, 0.3)
|
||
|
||
func _play_hide_animation():
|
||
"""播放隐藏动画"""
|
||
var tween = create_tween()
|
||
tween.tween_property(self, "modulate:a", 0.0, 0.3)
|
||
```
|
||
|
||
#### 游戏逻辑模块示例
|
||
```gdscript
|
||
# 游戏逻辑模块
|
||
extends Node
|
||
class_name GameLogicModule
|
||
|
||
signal state_changed(old_state: String, new_state: String)
|
||
signal data_updated(data_type: String, new_data: Dictionary)
|
||
|
||
var module_data: Dictionary = {}
|
||
var current_state: String = "idle"
|
||
|
||
func process_game_logic(delta: float):
|
||
"""处理游戏逻辑"""
|
||
match current_state:
|
||
"idle":
|
||
_process_idle_state(delta)
|
||
"active":
|
||
_process_active_state(delta)
|
||
"paused":
|
||
_process_paused_state(delta)
|
||
|
||
func change_state(new_state: String):
|
||
"""改变模块状态"""
|
||
var old_state = current_state
|
||
current_state = new_state
|
||
state_changed.emit(old_state, new_state)
|
||
|
||
func update_data(data_type: String, new_data: Dictionary):
|
||
"""更新模块数据"""
|
||
module_data[data_type] = new_data
|
||
data_updated.emit(data_type, new_data)
|
||
```
|
||
|
||
### 第五步:添加测试
|
||
|
||
```gdscript
|
||
# tests/test_your_module.gd
|
||
extends "res://addons/gut/test.gd"
|
||
|
||
var module: YourModule
|
||
|
||
func before_each():
|
||
module = YourModule.new()
|
||
add_child(module)
|
||
|
||
func after_each():
|
||
module.queue_free()
|
||
|
||
func test_module_initialization():
|
||
assert_true(module.initialize(), "模块应该能够正确初始化")
|
||
assert_eq(module.current_state, YourModule.ModuleState.READY, "初始化后状态应该为READY")
|
||
|
||
func test_module_cleanup():
|
||
module.initialize()
|
||
module.cleanup()
|
||
assert_eq(module.current_state, YourModule.ModuleState.UNINITIALIZED, "清理后状态应该为UNINITIALIZED")
|
||
|
||
func test_module_info():
|
||
var info = module.get_module_info()
|
||
assert_true(info.has("name"), "模块信息应该包含名称")
|
||
assert_true(info.has("version"), "模块信息应该包含版本")
|
||
assert_eq(info.name, "YourModule", "模块名称应该正确")
|
||
```
|
||
|
||
### 第六步:编写文档
|
||
|
||
```markdown
|
||
# YourModule 模块
|
||
|
||
## 功能描述
|
||
简要描述模块的主要功能和用途。
|
||
|
||
## 使用方法
|
||
```gdscript
|
||
# 创建和初始化模块
|
||
var your_module = YourModule.new()
|
||
add_child(your_module)
|
||
your_module.initialize()
|
||
|
||
# 使用模块功能
|
||
your_module.some_function()
|
||
```
|
||
|
||
## API参考
|
||
### 公共方法
|
||
- `initialize() -> bool` - 初始化模块
|
||
- `cleanup()` - 清理模块资源
|
||
- `get_module_info() -> Dictionary` - 获取模块信息
|
||
|
||
### 信号
|
||
- `signal_name(param: Type)` - 信号描述
|
||
|
||
## 配置选项
|
||
描述模块的配置选项和默认值。
|
||
|
||
## 依赖关系
|
||
列出模块的依赖项。
|
||
|
||
## 注意事项
|
||
使用模块时需要注意的事项。
|
||
```
|
||
|
||
## 🔧 模块集成
|
||
|
||
### 在场景中使用模块
|
||
|
||
```gdscript
|
||
# 在场景脚本中使用模块
|
||
extends Control
|
||
|
||
var inventory_module: InventoryModule
|
||
var dialogue_module: DialogueModule
|
||
|
||
func _ready():
|
||
# 创建并初始化模块
|
||
inventory_module = InventoryModule.new()
|
||
add_child(inventory_module)
|
||
inventory_module.initialize()
|
||
|
||
dialogue_module = DialogueModule.new()
|
||
add_child(dialogue_module)
|
||
dialogue_module.initialize()
|
||
|
||
# 连接模块事件
|
||
inventory_module.item_selected.connect(_on_item_selected)
|
||
dialogue_module.dialogue_finished.connect(_on_dialogue_finished)
|
||
|
||
func _on_item_selected(item_data: Dictionary):
|
||
print("选中物品: ", item_data.name)
|
||
|
||
func _on_dialogue_finished():
|
||
print("对话结束")
|
||
```
|
||
|
||
### 模块间通信
|
||
|
||
推荐使用EventSystem进行模块间通信:
|
||
|
||
```gdscript
|
||
# 发送事件
|
||
EventSystem.emit_event("inventory_item_used", {
|
||
"item_id": "potion_001",
|
||
"quantity": 1
|
||
})
|
||
|
||
# 监听事件
|
||
EventSystem.connect_event("inventory_item_used", _on_item_used)
|
||
|
||
func _on_item_used(data: Dictionary):
|
||
print("使用了物品: ", data.item_id)
|
||
```
|
||
|
||
## 🧪 测试和调试
|
||
|
||
### 单元测试
|
||
每个模块都应该有对应的单元测试:
|
||
|
||
```bash
|
||
# 运行模块测试
|
||
godot --headless --script tests/test_your_module.gd
|
||
```
|
||
|
||
### 调试技巧
|
||
1. **使用print语句** - 在关键位置添加调试输出
|
||
2. **断点调试** - 在Godot编辑器中设置断点
|
||
3. **状态监控** - 实时监控模块状态变化
|
||
4. **事件追踪** - 记录事件的发送和接收
|
||
|
||
### 性能监控
|
||
```gdscript
|
||
# 性能监控示例
|
||
func _process(delta):
|
||
var start_time = Time.get_time_dict_from_system()
|
||
|
||
# 执行模块逻辑
|
||
process_module_logic(delta)
|
||
|
||
var end_time = Time.get_time_dict_from_system()
|
||
var execution_time = (end_time.hour * 3600 + end_time.minute * 60 + end_time.second) - \
|
||
(start_time.hour * 3600 + start_time.minute * 60 + start_time.second)
|
||
|
||
if execution_time > 0.016: # 超过16ms
|
||
print("警告: 模块执行时间过长: ", execution_time, "秒")
|
||
```
|
||
|
||
## 📚 最佳实践
|
||
|
||
### 代码质量
|
||
1. **遵循命名规范** - 使用清晰、一致的命名
|
||
2. **添加详细注释** - 解释复杂逻辑和设计决策
|
||
3. **错误处理** - 妥善处理各种异常情况
|
||
4. **资源管理** - 及时释放不需要的资源
|
||
|
||
### 性能优化
|
||
1. **避免频繁的内存分配** - 重用对象和数据结构
|
||
2. **合理使用信号** - 避免过多的信号连接
|
||
3. **批量处理** - 将多个操作合并处理
|
||
4. **延迟加载** - 按需加载资源和数据
|
||
|
||
### 可维护性
|
||
1. **模块化设计** - 保持模块的独立性
|
||
2. **版本控制** - 记录模块的版本变化
|
||
3. **文档更新** - 及时更新模块文档
|
||
4. **向后兼容** - 考虑API的向后兼容性
|
||
|
||
## 🔍 常见问题
|
||
|
||
### Q: 如何处理模块依赖?
|
||
A: 在模块配置中声明依赖,在初始化时检查依赖是否满足。
|
||
|
||
### Q: 模块间如何共享数据?
|
||
A: 使用EventSystem传递数据,或通过GameManager等全局管理器。
|
||
|
||
### Q: 如何调试模块问题?
|
||
A: 使用Godot的调试工具,添加日志输出,编写单元测试。
|
||
|
||
### Q: 模块性能如何优化?
|
||
A: 避免在_process中执行重复计算,使用对象池,合理管理资源。
|
||
|
||
## 📖 参考资料
|
||
|
||
- [Godot官方文档](https://docs.godotengine.org/)
|
||
- [项目命名规范](./naming_convention.md)
|
||
- [代码注释规范](./code_comment_guide.md)
|
||
- [Git提交规范](./git_commit_guide.md)
|
||
|
||
---
|
||
|
||
**记住:好的模块设计是项目成功的关键!** |