forked from datawhale/whale-town-front
- 修复README.md中的emoji字符显示问题 - 移除文档质量评级系统 - 添加贡献者致谢部分,创建详细的CONTRIBUTORS.md - 创建核心系统文件EventNames.gd和ProjectPaths.gd - 更新项目配置文件project.godot,添加输入映射 - 完善各模块文档,修正路径引用问题 - 创建文档更新日志CHANGELOG.md - 优化文档结构和导航系统
11 KiB
11 KiB
模块开发指南
本文档详细说明如何在 Whale Town 项目中开发新的游戏模块。
🎯 模块设计原则
核心原则
- 单一职责 - 每个模块只负责一个特定的游戏功能
- 高内聚低耦合 - 模块内部紧密相关,模块间松散耦合
- 可复用性 - 模块应该能在不同场景中复用
- 标准化接口 - 统一的模块接口和通信方式
- 测试友好 - 模块应该易于测试和调试
模块分类
- 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
📝 模块开发步骤
第一步:创建模块结构
# 创建模块目录
mkdir module/YourModule
cd module/YourModule
# 创建子目录
mkdir components data scenes tests
# 创建主要文件
touch YourModule.gd
touch README.md
touch data/module_config.json
第二步:实现模块接口
每个模块都应该实现标准的模块接口:
# 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 = ProjectPaths.get_module_config_path(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
第三步:创建模块配置
// 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模块示例
# 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)
游戏逻辑模块示例
# 游戏逻辑模块
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)
第五步:添加测试
# tests/test_your_module.gd
extends GutTest
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", "模块名称应该正确")
第六步:编写文档
# 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进行模块间通信:
# 发送事件
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)
🧪 测试和调试
单元测试
每个模块都应该有对应的单元测试:
# 运行模块测试
godot --headless --script tests/test_your_module.gd
调试技巧
- 使用print语句 - 在关键位置添加调试输出
- 断点调试 - 在Godot编辑器中设置断点
- 状态监控 - 实时监控模块状态变化
- 事件追踪 - 记录事件的发送和接收
性能监控
# 性能监控示例
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, "秒")
📚 最佳实践
代码质量
- 遵循命名规范 - 使用清晰、一致的命名
- 添加详细注释 - 解释复杂逻辑和设计决策
- 错误处理 - 妥善处理各种异常情况
- 资源管理 - 及时释放不需要的资源
性能优化
- 避免频繁的内存分配 - 重用对象和数据结构
- 合理使用信号 - 避免过多的信号连接
- 批量处理 - 将多个操作合并处理
- 延迟加载 - 按需加载资源和数据
可维护性
- 模块化设计 - 保持模块的独立性
- 版本控制 - 记录模块的版本变化
- 文档更新 - 及时更新模块文档
- 向后兼容 - 考虑API的向后兼容性
🔍 常见问题
Q: 如何处理模块依赖?
A: 在模块配置中声明依赖,在初始化时检查依赖是否满足。
Q: 模块间如何共享数据?
A: 使用EventSystem传递数据,或通过GameManager等全局管理器。
Q: 如何调试模块问题?
A: 使用Godot的调试工具,添加日志输出,编写单元测试。
Q: 模块性能如何优化?
A: 避免在_process中执行重复计算,使用对象池,合理管理资源。
📖 参考资料
记住:好的模块设计是项目成功的关键!