# 模块开发指南 本文档详细说明如何在 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 = 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 ``` ### 第三步:创建模块配置 ```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 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", "模块名称应该正确") ``` ### 第六步:编写文档 ```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) --- **记住:好的模块设计是项目成功的关键!**