Files
whale-town-front/docs/04-高级开发/模块开发指南.md
moyin 0edd1c740b docs: 完善项目文档和README,修复字符显示问题
- 修复README.md中的emoji字符显示问题
- 移除文档质量评级系统
- 添加贡献者致谢部分,创建详细的CONTRIBUTORS.md
- 创建核心系统文件EventNames.gd和ProjectPaths.gd
- 更新项目配置文件project.godot,添加输入映射
- 完善各模块文档,修正路径引用问题
- 创建文档更新日志CHANGELOG.md
- 优化文档结构和导航系统
2025-12-31 18:58:38 +08:00

11 KiB
Raw Blame History

模块开发指南

本文档详细说明如何在 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

📝 模块开发步骤

第一步:创建模块结构

# 创建模块目录
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

调试技巧

  1. 使用print语句 - 在关键位置添加调试输出
  2. 断点调试 - 在Godot编辑器中设置断点
  3. 状态监控 - 实时监控模块状态变化
  4. 事件追踪 - 记录事件的发送和接收

性能监控

# 性能监控示例
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中执行重复计算使用对象池合理管理资源。

📖 参考资料


记住:好的模块设计是项目成功的关键!