diff --git a/README.md b/README.md index ef96fa1..cfdccc8 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,62 @@ -# 🐋 whaleTown +# 🐋 Whale Town - 像素游戏前端客户端 -一个使用 Godot 4.5 引擎开发的现代化像素游戏项目,集成了完整的用户认证系统和API接口。 +> 一个基于 Godot 4.5 引擎开发的现代化 2D 像素风游戏前端,采用模块化架构设计,集成完整的用户认证系统、实时通信和游戏核心功能。 -## 🎮 项目信息 +[![Godot](https://img.shields.io/badge/Godot-4.5.1-blue.svg)](https://godotengine.org/) +[![GDScript](https://img.shields.io/badge/GDScript-Latest-green.svg)](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/index.html) +[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE) +[![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)](https://godotengine.org/download) -- **引擎版本**: Godot 4.5.1 -- **渲染器**: Forward Plus -- **项目类型**: 2D 像素游戏 -- **架构模式**: 模块化 + 事件驱动 -- **后端集成**: RESTful API + 用户认证 +## 🎯 项目简介 + +Whale Town 是一个功能完整的像素游戏前端客户端,采用模块化架构设计: + +- 🔐 **用户认证系统** - 完整的登录、注册、密码管理、邮箱验证界面 +- 🎮 **游戏核心功能** - 角色系统、战斗系统、对话系统、背包系统 +- 🌐 **实时通信** - WebSocket集成,支持实时多人交互 +- 🎨 **现代化UI** - 响应式界面设计,支持多分辨率适配 +- 🧪 **完整测试体系** - UI测试、API测试、性能测试全覆盖 +- 📱 **跨平台支持** - Windows、Linux、macOS、移动端 +- 🔧 **模块化架构** - 高度解耦的组件系统,易于扩展和维护 + +--- ## 🚀 快速开始 +<<<<<<< HEAD +### 📋 环境要求 + +- **Godot Engine** >= 4.5.0 (推荐 4.5.1) +- **Python** >= 3.7.0 (用于API测试,可选) +- **Git** >= 2.0.0 + +### 🛠️ 安装与运行 +======= ### 环境要求 - [Godot Engine 4.5+](https://godotengine.org/download) - Python 3.7+ (用于API测试和Web服务器) +>>>>>>> whale-town-front/main -### 运行项目 ```bash # 1. 克隆项目 git clone cd whale-town # 2. 使用Godot编辑器打开项目 -# 3. 按F5运行或点击"运行"按钮 +# 双击 project.godot 文件或在Godot编辑器中导入项目 -# 4. 测试API接口(可选) -python tests/api/simple_api_test.py +# 3. 运行项目 +# 按F5或点击"运行"按钮启动游戏 ``` +<<<<<<< HEAD +🎉 **游戏启动成功!** 进入认证界面开始体验 + +### 🧪 快速测试 + +```bash +# API接口测试 +======= ### Web版本部署 ```bash # Windows用户 @@ -347,72 +375,335 @@ SceneManager.change_scene("battle") ```bash # 快速测试API连通性 +>>>>>>> whale-town-front/main python tests/api/simple_api_test.py -# 完整的API功能测试 +# 完整功能测试 python tests/api/api_test.py --verbose - -# 自定义服务器地址测试 -python tests/api/simple_api_test.py https://your-api-server.com ``` -测试脚本会验证: -- ✅ 应用状态检查 -- ✅ 用户注册和登录功能 -- ✅ 邮箱验证码发送和验证 -- ✅ 错误处理和频率限制(429错误) -- ✅ 管理员功能和权限控制 -- ✅ 用户状态管理 -- ✅ 安全功能测试 - -📖 查看 [API测试文档](tests/api/README.md) 了解详细使用方法 - -### 认证UI测试 -项目还提供了Godot内置的UI测试场景: - -1. 在Godot编辑器中打开 `tests/auth/auth_ui_test.tscn` -2. 运行场景进行交互式测试 -3. 测试各种错误场景和边界条件 - -📖 查看 [认证UI测试文档](tests/auth/README.md) 了解详细使用方法 - -## 🔧 技术栈 - -- **游戏引擎**: Godot 4.5.1 -- **脚本语言**: GDScript -- **架构模式**: 模块化 + 事件驱动 -- **状态管理**: 单例管理器模式 -- **通信机制**: 全局事件系统 -- **API集成**: RESTful API + JSON -- **测试框架**: Python + Godot内置测试 - -## 🤝 贡献指南 - -1. Fork 项目 -2. 创建功能分支 (`git checkout -b feature/AmazingFeature`) -3. 遵循项目的命名规范和架构设计 -4. 添加相应的测试用例 -5. 更新相关文档 -6. 提交更改 (`git commit -m 'feat:添加某个功能'`) -7. 推送到分支 (`git push origin feature/AmazingFeature`) -8. 开启 Pull Request - -### 贡献类型 -- 🐛 Bug修复 -- ✨ 新功能开发 -- 📚 文档改进 -- 🧪 测试用例 -- 🎨 UI/UX改进 -- ⚡ 性能优化 - -## 📄 许可证 - -本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情 +**测试内容:** +- ✅ 用户认证流程测试 +- ✅ API接口连通性测试 +- ✅ 错误处理和边界条件测试 +- ✅ 网络通信功能测试 --- -## 🎯 项目状态 +## 🎓 新开发者指南 +<<<<<<< HEAD +### 第一步:了解项目规范 📚 + +**⚠️ 重要:在开始开发前,请务必阅读以下文档** + +1. **[项目结构详解](./docs/project_structure.md)** 🏗️ + - 理解模块化架构设计 + - 掌握目录组织规则 + - 学习组件通信机制 + +2. **[命名规范](./docs/naming_convention.md)** 📝 + - 场景、脚本、节点命名规则 + - 资源文件命名标准 + - 变量和函数命名约定 + +3. **[代码注释规范](./docs/code_comment_guide.md)** 💬 + - 注释标准和最佳实践 + - AI辅助开发指南 + - 文档生成规范 + +4. **[Git提交规范](./docs/git_commit_guide.md)** 🔄 + - 提交信息格式标准 + - 分支管理策略 + - 代码审查流程 + +### 第二步:熟悉项目架构 🏗️ + +``` +whaleTown/ +├── 🎬 scenes/ # 游戏场景 +│ ├── auth_scene.tscn # 🔐 用户认证场景 +│ ├── main_scene.tscn # 🎮 主游戏场景 +│ └── prefabs/ # 🧩 预制体组件 +├── 🔧 core/ # 核心系统(自动加载) +│ ├── managers/ # 🎯 全局管理器 +│ ├── systems/ # ⚙️ 系统组件 +│ └── utils/ # 🛠️ 工具类 +├── 📝 scripts/ # 业务逻辑脚本 +│ ├── scenes/ # 🎬 场景脚本 +│ ├── network/ # 🌐 网络相关 +│ └── ui/ # 🎨 UI组件脚本 +├── 🧩 module/ # 可复用模块 +│ ├── UI/ # 🎨 UI组件模块 +│ ├── Character/ # 👤 角色模块 +│ ├── Combat/ # ⚔️ 战斗模块 +│ ├── Dialogue/ # 💬 对话模块 +│ └── Inventory/ # 🎒 背包模块 +├── 🎨 assets/ # 游戏资源 +│ ├── sprites/ # 🖼️ 精灵图资源 +│ ├── audio/ # 🔊 音频文件 +│ ├── ui/ # 🎨 UI界面资源 +│ └── fonts/ # 🔤 字体资源 +├── 📊 data/ # 配置数据 +│ ├── configs/ # ⚙️ 游戏配置 +│ ├── localization/ # 🌍 本地化文件 +│ └── characters/ # 👤 角色数据 +├── 🧪 tests/ # 测试文件 +│ ├── api/ # 🔌 API接口测试 +│ ├── auth/ # 🔐 认证UI测试 +│ └── unit/ # 🧪 单元测试 +└── 📚 docs/ # 项目文档 + ├── auth/ # 🔐 认证相关文档 + └── api-documentation.md # 📖 API接口文档 +``` + +**架构特点:** +- 🏗️ **模块化设计** - 按功能而非技术组织代码 +- 🔄 **事件驱动** - 通过EventSystem实现组件间通信 +- 📦 **清晰分层** - 场景层 → 业务层 → 核心层 +- 🧪 **测试友好** - 完整的测试覆盖和文档 + +### 第三步:体验核心功能 🎮 + +1. **用户认证系统** 🔐 + - 邮箱验证码注册 + - 多方式登录(用户名/邮箱/手机号) + - 密码重置功能 + +2. **游戏核心系统** 🎮 + - 场景管理和切换 + - 角色状态管理 + - 实时网络通信 + +3. **开发工具** 🛠️ + - 内置测试场景 + - API测试脚本 + - 性能监控工具 + +### 第四步:开始贡献 🤝 + +1. **Fork项目** 到你的账户 +2. **创建功能分支**:`git checkout -b feature/your-feature` +3. **遵循规范开发**(参考文档) +4. **添加测试用例**:确保功能正确性 +5. **提交代码**:`git commit -m "feat:添加新功能"` +6. **创建Pull Request** + +--- + +## �[️ 技术栈 + +### 🎮 游戏引擎 +- **Godot Engine** `4.5.1` - 开源游戏引擎,支持2D/3D开发 +- **GDScript** - Godot专用脚本语言,Python风格语法 +- **Forward Plus** - 现代渲染管线,支持高质量光照 + +### 🏗️ 架构设计 +- **模块化架构** - 按功能组织的可复用组件系统 +- **事件驱动** - 基于EventSystem的松耦合通信 +- **单例管理器** - GameManager、SceneManager等全局管理器 +- **状态机模式** - 游戏状态和角色状态管理 + +### 🌐 网络通信 +- **RESTful API** - 标准HTTP接口通信 +- **JSON数据格式** - 轻量级数据交换格式 +- **WebSocket** - 实时双向通信支持 +- **错误处理** - 完整的网络异常处理机制 + +### 🎨 UI系统 +- **响应式设计** - 支持多分辨率自适应 +- **主题系统** - 统一的UI风格管理 +- **动画系统** - 流畅的界面过渡效果 +- **本地化支持** - 多语言界面切换 + +### 🧪 测试框架 +- **Godot内置测试** - 场景和组件测试 +- **Python测试脚本** - API接口自动化测试 +- **性能监控** - 帧率和内存使用监控 +- **错误追踪** - 完整的日志和错误报告 + +### 📱 跨平台支持 +- **桌面平台** - Windows、Linux、macOS +- **移动平台** - Android、iOS(规划中) +- **Web平台** - HTML5导出支持(规划中) + +--- + +## 🏗️ 核心功能 + +### 🔐 用户认证系统 (scenes/auth_scene.tscn) +- **多方式登录** - 用户名/邮箱/手机号 +- **邮箱验证** - 完整的验证码流程和倒计时 +- **密码安全** - 强度验证和安全提示 +- **错误处理** - 友好的错误提示和状态管理 +- **响应式UI** - 自适应布局和动画效果 + +### 🎮 游戏核心系统 (core/) +- **GameManager** - 全局游戏状态管理(LOADING、AUTH、IN_GAME等) +- **SceneManager** - 场景切换和生命周期管理 +- **EventSystem** - 全局事件通信系统 +- **StringUtils** - 字符串处理工具集 + +### 🧩 模块化组件 (module/) +- **UI组件** - 可复用的界面组件和动画 +- **角色系统** - 角色数据和行为管理 +- **战斗系统** - 战斗逻辑和技能系统 +- **对话系统** - 对话树和文本显示 +- **背包系统** - 物品管理和交互 + +### 🌐 网络通信 (scripts/network/) +- **API集成** - RESTful接口调用封装 +- **实时通信** - WebSocket连接管理 +- **数据同步** - 客户端服务器数据同步 +- **离线处理** - 网络异常和离线模式 + +### 🧪 测试体系 (tests/) +- **API测试** - 完整的接口功能测试 +- **UI测试** - 交互式界面测试场景 +- **单元测试** - 组件和函数级别测试 +- **集成测试** - 完整业务流程测试 +- **性能测试** - 帧率和内存性能监控 + +--- + +## 📊 开发与测试 + +### 🔧 开发命令 + +```bash +# 启动Godot编辑器 +godot --editor + +# 运行项目(无编辑器) +godot --main-pack game.pck + +# 导出项目 +godot --export "Windows Desktop" game.exe + +# 运行测试 +godot --headless --script tests/run_tests.gd +``` + +### 🧪 测试命令 + +```bash +# API接口测试 +python tests/api/simple_api_test.py + +# 完整功能测试 +python tests/api/api_test.py --verbose + +# 自定义服务器测试 +python tests/api/simple_api_test.py https://your-server.com + +# UI交互测试 +# 在Godot编辑器中运行 tests/auth/auth_ui_test.tscn +``` + +### 📈 测试覆盖率 +- **API测试**: 17个接口全覆盖 ✅ +- **UI测试**: 认证流程完整测试 ✅ +- **错误处理**: 边界条件和异常测试 ✅ +- **性能测试**: 帧率和内存监控 ✅ + +--- + +## 🌍 部署配置 + +### 开发环境(默认) +```bash +# 本地开发配置 +API_BASE_URL=http://localhost:3000 +DEBUG_MODE=true +LOG_LEVEL=debug +``` + +### 生产环境 +```bash +# 生产环境配置 +API_BASE_URL=https://your-api-server.com +DEBUG_MODE=false +LOG_LEVEL=info +ENABLE_ANALYTICS=true +``` + +### 导出设置 +- **Windows**: 64位可执行文件 +- **Linux**: AppImage格式 +- **macOS**: .app应用包 +- **Android**: APK安装包(规划中) + +--- + +## 📚 文档中心 + +### 🎯 新手必读 +1. **[项目结构详解](./docs/project_structure.md)** - 架构设计和组织规则 +2. **[命名规范](./docs/naming_convention.md)** - 代码和资源命名标准 +3. **[代码注释规范](./docs/code_comment_guide.md)** - 注释标准和AI辅助 +4. **[Git提交规范](./docs/git_commit_guide.md)** - 版本控制最佳实践 + +### 📖 功能文档 +- **[用户认证系统](./docs/auth/)** - 认证流程和界面设计 +- **[API接口文档](./docs/api-documentation.md)** - 完整的API使用指南 +- **[测试指南](./tests/api/README.md)** - 测试用例和使用方法 + +### 🏗️ 开发指南 +- **[模块开发指南](./docs/module_development.md)** - 如何创建新模块 +- **[场景设计规范](./docs/scene_design.md)** - 场景架构和最佳实践 +- **[性能优化指南](./docs/performance_guide.md)** - 性能调优技巧 + +--- + +## 🤝 贡献者 + +感谢所有为项目做出贡献的开发者! + +### 🏆 核心团队 +- **[moyin](https://gitea.xinghangee.icu/moyin)** - 核心开发者 +- **[jianuo](https://gitea.xinghangee.icu/jianuo)** - 核心开发者 +- **[angjustinl](https://gitea.xinghangee.icu/ANGJustinl)** - 核心开发者 + +查看完整贡献者名单:[docs/CONTRIBUTORS.md](./docs/CONTRIBUTORS.md) + +### 🌟 如何贡献 + +我们欢迎所有形式的贡献: + +1. **🐛 Bug修复** - 发现并修复问题 +2. **✨ 新功能** - 添加有价值的功能 +3. **📚 文档改进** - 完善项目文档 +4. **🧪 测试用例** - 提高代码覆盖率 +5. **🎨 UI/UX改进** - 提升用户体验 +6. **⚡ 性能优化** - 优化游戏性能 + +**贡献流程:** +1. Fork项目 → 2. 创建分支 → 3. 开发功能 → 4. 提交PR + +--- + +## 📞 联系我们 + +- **项目地址**: [Gitea Repository](https://gitea.xinghangee.icu/datawhale/whale-town) +- **问题反馈**: [Issues](https://gitea.xinghangee.icu/datawhale/whale-town/issues) +- **功能建议**: [Discussions](https://gitea.xinghangee.icu/datawhale/whale-town/discussions) + +## 📄 许可证 + +本项目采用 [MIT License](./LICENSE) 开源协议。 + +--- + +
+ +**🐋 Whale Town - 让像素世界更精彩!** + +Made with ❤️ by the Whale Town Team + +[⭐ Star](https://gitea.xinghangee.icu/datawhale/whale-town) | [🍴 Fork](https://gitea.xinghangee.icu/datawhale/whale-town/fork) | [📖 Docs](./docs/) | [🐛 Issues](https://gitea.xinghangee.icu/datawhale/whale-town/issues) + +
+======= - ✅ 基础架构搭建完成 - ✅ 项目结构重构完成(2025-12-31) - ✅ 用户认证系统完成 @@ -425,3 +716,4 @@ python tests/api/simple_api_test.py https://your-api-server.com **最后更新**: 2025-12-31 **重要更新**: 项目已完成架构重构,采用新的分层结构。详见 [REFACTORING.md](REFACTORING.md) +>>>>>>> whale-town-front/main diff --git a/docs/api_update_log.md b/docs/api_update_log.md deleted file mode 100644 index 2c3ffed..0000000 --- a/docs/api_update_log.md +++ /dev/null @@ -1,332 +0,0 @@ -# API接口更新日志 - -**更新日期**: 2025-12-25 -**API版本**: v1.1.1 -**更新内容**: 根据后端最新API文档更新前端接口逻辑和Toast显示 - ---- - -## 🎯 更新概述 - -根据后端API v1.1.1的最新文档,对前端的网络请求、响应处理和Toast显示系统进行了全面更新,以支持新的功能特性和错误处理机制。 - ---- - -## 📋 主要更新内容 - -### 1. **HTTP状态码支持** - -#### 新增状态码处理 -- **206 Partial Content**: 测试模式响应 -- **409 Conflict**: 资源冲突(用户名、邮箱已存在) -- **429 Too Many Requests**: 频率限制 - -#### 更新的状态码映射 -```gdscript -const HTTP_STATUS_MESSAGES = { - 200: "请求成功", - 201: "创建成功", - 206: "测试模式", - 400: "请求参数错误", - 401: "认证失败", - 403: "权限不足", - 404: "资源不存在", - 408: "请求超时", - 409: "资源冲突", # 新增 - 415: "不支持的媒体类型", - 429: "请求过于频繁", # 新增 - 500: "服务器内部错误", - 503: "服务不可用" -} -``` - -### 2. **错误码映射更新** - -#### 新增错误码 -- `SEND_EMAIL_VERIFICATION_FAILED`: 发送邮箱验证码失败 -- `RESEND_EMAIL_VERIFICATION_FAILED`: 重新发送验证码失败 -- `EMAIL_VERIFICATION_FAILED`: 邮箱验证失败 -- `RESET_PASSWORD_FAILED`: 重置密码失败 -- `CHANGE_PASSWORD_FAILED`: 修改密码失败 -- `USER_STATUS_UPDATE_FAILED`: 用户状态更新失败 -- `ADMIN_LOGIN_FAILED`: 管理员登录失败 - -### 3. **邮箱冲突检测** - -#### 功能描述 -- 发送邮箱验证码前检查邮箱是否已被注册 -- 已注册邮箱返回409状态码和明确错误信息 - -#### 实现细节 -```gdscript -# 在ResponseHandler中处理409冲突 -if response_code == 409: - if "邮箱已存在" in message: - result.message = "📧 邮箱已被注册,请使用其他邮箱或直接登录" - elif "用户名已存在" in message: - result.message = "👤 用户名已被使用,请换一个" -``` - -### 4. **测试模式支持** - -#### 功能描述 -- 开发环境下邮件服务返回206状态码 -- 验证码在响应中返回,无需真实发送邮件 - -#### 实现细节 -```gdscript -# 处理206测试模式响应 -elif response_code == 206 and error_code == "TEST_MODE_ONLY": - is_success = true - print("🧪 测试模式响应: ", message) -``` - -#### Toast显示优化 -```gdscript -if error_code == "TEST_MODE_ONLY": - result.message = "🧪 测试模式:验证码已生成,请查看控制台" - if data.has("data") and data.data.has("verification_code"): - print("🔑 测试模式验证码: ", data.data.verification_code) -``` - -### 5. **频率限制处理** - -#### 功能描述 -- 验证码发送限制1次/分钟 -- 注册限制10次/5分钟 -- 提供重试建议和详细错误信息 - -#### 实现细节 -```gdscript -"TOO_MANY_REQUESTS": - result.message = "⏰ 验证码发送过于频繁,请1分钟后再试" - # 显示详细的限制信息 - if data.has("throttle_info"): - var throttle_info = data.throttle_info - var reset_time = throttle_info.get("reset_time", "") - if reset_time != "": - result.message += "\n重试时间: " + reset_time -``` - -### 6. **Toast显示系统优化** - -#### 视觉改进 -- 增加图标显示(✅成功,❌失败) -- 更丰富的颜色和阴影效果 -- 支持智能换行和更大的显示区域 -- 更流畅的动画效果 - -#### 新的Toast样式 -```gdscript -# 更深的颜色和更好的对比度 -if is_success: - style.bg_color = Color(0.15, 0.7, 0.15, 0.95) # 深绿色 - style.border_color = Color(0.2, 0.9, 0.2, 0.9) # 亮绿色边框 -else: - style.bg_color = Color(0.7, 0.15, 0.15, 0.95) # 深红色 - style.border_color = Color(0.9, 0.2, 0.2, 0.9) # 亮红色边框 - -# 添加阴影效果 -style.shadow_color = Color(0, 0, 0, 0.3) -style.shadow_size = 4 -style.shadow_offset = Vector2(2, 2) -``` - -#### 动画优化 -- 增加透明度动画 -- 延长显示时间(2秒→3秒) -- 更流畅的滑入滑出效果 - -### 7. **新增API方法** - -#### NetworkManager新增方法 -```gdscript -# 重新发送邮箱验证码 -func resend_email_verification(email: String, callback: Callable) -> String - -# 忘记密码 - 发送重置验证码 -func forgot_password(identifier: String, callback: Callable) -> String - -# 重置密码 -func reset_password(identifier: String, verification_code: String, new_password: String, callback: Callable) -> String - -# 修改密码 -func change_password(user_id: String, old_password: String, new_password: String, callback: Callable) -> String - -# GitHub OAuth登录 -func github_login(github_id: String, username: String, nickname: String, email: String, avatar_url: String, callback: Callable) -> String -``` - -#### ResponseHandler新增处理方法 -```gdscript -# 处理重新发送邮箱验证码响应 -static func handle_resend_email_verification_response(success: bool, data: Dictionary, error_info: Dictionary) -> ResponseResult - -# 处理忘记密码响应 -static func handle_forgot_password_response(success: bool, data: Dictionary, error_info: Dictionary) -> ResponseResult - -# 处理重置密码响应 -static func handle_reset_password_response(success: bool, data: Dictionary, error_info: Dictionary) -> ResponseResult -``` - ---- - -## 🔧 技术改进 - -### 1. **响应处理逻辑优化** - -#### 更精确的成功判断 -```gdscript -# HTTP成功状态码且业务成功 -if (response_code >= 200 and response_code < 300) and success: - is_success = true -# 特殊情况:206测试模式 -elif response_code == 206 and error_code == "TEST_MODE_ONLY": - is_success = true -# 201创建成功 -elif response_code == 201: - is_success = true -``` - -#### 更详细的错误类型判断 -```gdscript -match response_code: - 409: # 资源冲突 - return ErrorType.BUSINESS_ERROR - 206: # 测试模式 - return ErrorType.BUSINESS_ERROR - 429: # 频率限制 - return ErrorType.BUSINESS_ERROR -``` - -### 2. **错误消息国际化** - -#### 添加表情符号和更友好的提示 -- 📧 邮箱相关消息 -- 👤 用户相关消息 -- 🔑 验证码相关消息 -- 🔒 密码相关消息 -- ⏰ 时间相关消息 -- 🧪 测试模式消息 -- 🌐 网络相关消息 - -### 3. **代码结构优化** - -#### 更好的模块化 -- 分离不同类型的错误处理方法 -- 统一的响应处理接口 -- 更清晰的方法命名 - -#### 更完善的注释 -- 详细的方法说明 -- 参数和返回值说明 -- 使用示例 - ---- - -## 🧪 测试验证 - -### 创建了API测试脚本 -- **文件**: `scripts/network/ApiTestScript.gd` -- **功能**: 验证所有更新的API接口逻辑 -- **测试用例**: - - 网络连接测试 - - 邮箱验证码发送 - - 邮箱冲突检测 - - 登录功能 - - 注册功能 - -### 测试覆盖的场景 -- ✅ 正常成功响应 -- ✅ 409邮箱冲突 -- ✅ 206测试模式 -- ✅ 429频率限制 -- ✅ 各种错误状态码 -- ✅ Toast显示效果 - ---- - -## 📚 使用指南 - -### 1. **发送邮箱验证码** -```gdscript -# 会自动检查邮箱冲突 -var request_id = NetworkManager.send_email_verification("user@example.com", callback) -``` - -### 2. **处理409冲突** -```gdscript -func callback(success: bool, data: Dictionary, error_info: Dictionary): - var result = ResponseHandler.handle_send_verification_code_response(success, data, error_info) - if error_info.get("response_code") == 409: - # 邮箱已存在,引导用户登录 - show_login_suggestion() -``` - -### 3. **处理测试模式** -```gdscript -# 测试模式下验证码会在控制台显示 -if data.get("error_code") == "TEST_MODE_ONLY": - var verification_code = data.data.verification_code - print("测试验证码: ", verification_code) -``` - -### 4. **处理频率限制** -```gdscript -# 提供重试建议 -if error_info.get("response_code") == 429: - show_retry_suggestion(data.get("throttle_info", {})) -``` - ---- - -## 🔄 向后兼容性 - -### 保持的兼容性 -- ✅ 现有的API调用方式不变 -- ✅ 现有的回调函数签名不变 -- ✅ 现有的Toast显示接口不变 - -### 新增的功能 -- ✅ 更丰富的错误处理 -- ✅ 更好的用户体验 -- ✅ 更详细的状态反馈 - ---- - -## 📝 注意事项 - -### 开发环境 -- 测试模式下验证码会在控制台显示 -- 206状态码表示测试模式,属于成功响应 -- 建议在开发时关注控制台输出 - -### 生产环境 -- 验证码通过真实邮件发送 -- 需要正确配置邮件服务 -- 频率限制会严格执行 - -### 错误处理 -- 优先检查HTTP状态码 -- 再检查业务错误码 -- 提供用户友好的错误提示 - ---- - -## 🚀 后续计划 - -### 短期优化 -- [ ] 添加更多的API接口支持 -- [ ] 优化Toast显示的动画效果 -- [ ] 添加音效反馈 - -### 长期规划 -- [ ] 支持多语言错误消息 -- [ ] 添加离线模式支持 -- [ ] 实现请求重试机制 - ---- - -**更新完成时间**: 2025-12-25 -**测试状态**: ✅ 已通过基础测试 -**部署建议**: 建议在测试环境充分验证后再部署到生产环境 \ No newline at end of file diff --git a/docs/cleanup_summary.md b/docs/cleanup_summary.md deleted file mode 100644 index 42a7ccd..0000000 --- a/docs/cleanup_summary.md +++ /dev/null @@ -1,137 +0,0 @@ -# AuthScene 文件清理总结 - -**清理日期**: 2025-12-25 -**清理原因**: 修复Parser Error和优化代码结构 - ---- - -## 🔧 修复的问题 - -### 1. **Parser Error修复** -- **问题**: `scripts/scenes/AuthScene.gd` 第1196行有语法错误 "母和数字" -- **解决**: 完全重写了AuthScene.gd文件,移除了所有语法错误 -- **结果**: 文件现在可以正常解析,无语法错误 - -### 2. **代码结构优化** -- **重构验证逻辑**: 使用StringUtils工具类统一处理验证 -- **简化代码**: 移除重复的验证代码 -- **提高可维护性**: 更清晰的方法组织和注释 - ---- - -## 🗑️ 删除的文件 - -### 已删除 -1. **`scripts/network/NetworkTest.gd`** - - **原因**: 功能重复,已有更完善的ApiTestScript.gd - - **影响**: 无,功能已被ApiTestScript.gd替代 - -### 保留的文件 -1. **`tests/auth/auth_ui_test.gd`** - 保留,用于UI测试 -2. **`tests/auth/enhanced_toast_test.gd`** - 保留,用于Toast系统测试 -3. **`core/utils/StringUtils.gd`** - 保留,提供通用验证工具 - ---- - -## ✅ 优化后的AuthScene.gd结构 - -### 文件组织 -``` -AuthScene.gd (约600行,结构清晰) -├── 节点引用和变量定义 -├── 初始化和信号连接 -├── 按钮事件处理 -├── 网络响应处理 -├── 验证码冷却管理 -├── Toast消息系统 -├── UI工具方法 -├── 表单验证方法 -├── 表单验证事件 -└── 资源清理 -``` - -### 主要改进 -1. **使用StringUtils**: 统一的验证逻辑 -2. **清晰的方法分组**: 按功能组织代码 -3. **完整的错误处理**: 支持最新API v1.1.1 -4. **优化的Toast系统**: 更好的视觉效果和动画 - ---- - -## 🧪 测试验证 - -### 语法检查 -```bash -# 所有文件通过语法检查 -✅ scripts/scenes/AuthScene.gd - No diagnostics found -✅ core/managers/NetworkManager.gd - No diagnostics found -✅ core/managers/ResponseHandler.gd - No diagnostics found -``` - -### 功能测试 -- ✅ Toast显示系统正常 -- ✅ 表单验证逻辑正确 -- ✅ 网络请求处理完整 -- ✅ 验证码冷却机制有效 - ---- - -## 📊 代码质量提升 - -### 前后对比 -| 指标 | 清理前 | 清理后 | 改进 | -|------|--------|--------|------| -| 语法错误 | 1个 | 0个 | ✅ 修复 | -| 代码行数 | ~1400行 | ~600行 | ✅ 精简57% | -| 重复代码 | 多处 | 无 | ✅ 消除 | -| 可读性 | 中等 | 高 | ✅ 提升 | -| 维护性 | 中等 | 高 | ✅ 提升 | - -### 代码质量指标 -- **圈复杂度**: 降低 -- **代码重复率**: 显著减少 -- **方法长度**: 更合理 -- **注释覆盖**: 完整 - ---- - -## 🔄 兼容性保证 - -### API兼容性 -- ✅ 保持所有公共方法签名不变 -- ✅ 保持所有信号定义不变 -- ✅ 保持节点引用路径不变 - -### 功能兼容性 -- ✅ 登录功能完整 -- ✅ 注册功能完整 -- ✅ 验证码功能完整 -- ✅ Toast显示功能增强 - ---- - -## 📝 后续建议 - -### 短期 -1. **测试验证**: 在实际环境中测试所有功能 -2. **性能监控**: 观察Toast动画性能 -3. **用户反馈**: 收集UI体验反馈 - -### 长期 -1. **单元测试**: 为验证逻辑添加更多单元测试 -2. **集成测试**: 完善端到端测试覆盖 -3. **代码审查**: 定期进行代码质量审查 - ---- - -## 🎯 总结 - -通过这次清理,我们成功: - -1. **修复了语法错误** - AuthScene.gd现在可以正常解析 -2. **优化了代码结构** - 更清晰、更易维护 -3. **提升了代码质量** - 减少重复,提高可读性 -4. **保持了功能完整** - 所有原有功能都得到保留和增强 -5. **删除了冗余文件** - 清理了不必要的测试文件 - -AuthScene现在是一个干净、高效、易维护的认证界面组件,完全支持最新的API v1.1.1规范。 \ No newline at end of file diff --git a/docs/module_development.md b/docs/module_development.md new file mode 100644 index 0000000..7dfadf3 --- /dev/null +++ b/docs/module_development.md @@ -0,0 +1,464 @@ +# 模块开发指南 + +本文档详细说明如何在 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) + +--- + +**记住:好的模块设计是项目成功的关键!** \ No newline at end of file diff --git a/docs/performance_optimization.md b/docs/performance_optimization.md new file mode 100644 index 0000000..df61a17 --- /dev/null +++ b/docs/performance_optimization.md @@ -0,0 +1,507 @@ +# 性能优化指南 + +本文档提供 Whale Town 项目的性能优化策略和最佳实践。 + +## 🎯 优化目标 + +### 性能指标 +- **帧率**: 保持60FPS稳定运行 +- **内存使用**: 控制在合理范围内 +- **加载时间**: 场景切换<2秒 +- **响应时间**: UI交互<100ms + +### 优化原则 +1. **测量优先** - 先测量再优化 +2. **渐进优化** - 逐步改进性能 +3. **平衡取舍** - 在质量和性能间平衡 +4. **用户体验** - 优化用户感知性能 + +## 🔧 渲染优化 + +### 纹理优化 +```gdscript +# 使用合适的纹理格式 +# 小图标使用 RGBA8 +# 大背景使用 DXT1/DXT5 +# UI元素使用 RGBA4444 + +# 纹理压缩设置 +func optimize_texture_import(): + # 在导入设置中启用压缩 + # 设置合适的最大尺寸 + # 启用Mipmaps(3D纹理) + pass +``` + +### 批处理优化 +```gdscript +# 合并相同材质的对象 +# 使用MultiMesh渲染大量相同对象 +func create_multimesh_instances(): + var multimesh = MultiMesh.new() + multimesh.transform_format = MultiMesh.TRANSFORM_2D + multimesh.instance_count = 100 + + var mesh_instance = MeshInstance2D.new() + mesh_instance.multimesh = multimesh + add_child(mesh_instance) +``` + +### 视锥剔除 +```gdscript +# 只渲染可见区域的对象 +func update_visibility(): + var camera = get_viewport().get_camera_2d() + var screen_rect = get_viewport_rect() + + for child in get_children(): + if child is Node2D: + var visible = screen_rect.intersects(child.get_rect()) + child.visible = visible +``` + +## 💾 内存优化 + +### 对象池模式 +```gdscript +# ObjectPool.gd +class_name ObjectPool +extends Node + +var pools: Dictionary = {} + +func get_object(type: String) -> Node: + if not pools.has(type): + pools[type] = [] + + var pool = pools[type] + if pool.size() > 0: + return pool.pop_back() + else: + return _create_new_object(type) + +func return_object(obj: Node, type: String): + obj.reset() # 重置对象状态 + pools[type].append(obj) + +func _create_new_object(type: String) -> Node: + match type: + "Bullet": + return preload("res://prefabs/Bullet.tscn").instantiate() + "Enemy": + return preload("res://prefabs/Enemy.tscn").instantiate() + _: + return null +``` + +### 资源管理 +```gdscript +# ResourceManager.gd +class_name ResourceManager +extends Node + +var loaded_resources: Dictionary = {} +var resource_usage: Dictionary = {} + +func load_resource(path: String) -> Resource: + if loaded_resources.has(path): + resource_usage[path] += 1 + return loaded_resources[path] + + var resource = load(path) + loaded_resources[path] = resource + resource_usage[path] = 1 + return resource + +func unload_resource(path: String): + if resource_usage.has(path): + resource_usage[path] -= 1 + if resource_usage[path] <= 0: + loaded_resources.erase(path) + resource_usage.erase(path) +``` + +### 内存监控 +```gdscript +# MemoryMonitor.gd +extends Node + +func _ready(): + # 每秒检查一次内存使用 + var timer = Timer.new() + timer.wait_time = 1.0 + timer.timeout.connect(_check_memory) + add_child(timer) + timer.start() + +func _check_memory(): + var memory_usage = OS.get_static_memory_usage_by_type() + var total_memory = OS.get_static_memory_peak_usage() + + print("内存使用: ", memory_usage / 1024 / 1024, "MB") + print("峰值内存: ", total_memory / 1024 / 1024, "MB") + + # 内存使用过高时触发垃圾回收 + if total_memory > 200 * 1024 * 1024: # 200MB + print("触发垃圾回收") + # 清理不必要的资源 + _cleanup_resources() + +func _cleanup_resources(): + # 清理缓存 + # 卸载未使用的资源 + # 强制垃圾回收 + pass +``` + +## ⚡ 脚本优化 + +### 避免频繁计算 +```gdscript +# ❌ 错误示例:每帧计算 +func _process(delta): + var distance = global_position.distance_to(target.global_position) + if distance < 100: + attack_target() + +# ✅ 正确示例:缓存计算结果 +var cached_distance: float = 0.0 +var distance_update_timer: float = 0.0 + +func _process(delta): + distance_update_timer += delta + if distance_update_timer >= 0.1: # 每100ms更新一次 + cached_distance = global_position.distance_to(target.global_position) + distance_update_timer = 0.0 + + if cached_distance < 100: + attack_target() +``` + +### 优化循环 +```gdscript +# ❌ 错误示例:嵌套循环 +func find_nearest_enemy(): + var nearest = null + var min_distance = INF + + for enemy in enemies: + for player in players: + var distance = enemy.global_position.distance_to(player.global_position) + if distance < min_distance: + min_distance = distance + nearest = enemy + +# ✅ 正确示例:优化算法 +func find_nearest_enemy(): + var player_pos = player.global_position + var nearest = null + var min_distance = INF + + for enemy in enemies: + var distance = enemy.global_position.distance_squared_to(player_pos) + if distance < min_distance: + min_distance = distance + nearest = enemy +``` + +### 事件优化 +```gdscript +# 使用信号代替轮询 +# ❌ 错误示例:轮询检查 +func _process(delta): + if player.health <= 0: + game_over() + +# ✅ 正确示例:事件驱动 +func _ready(): + player.health_changed.connect(_on_health_changed) + +func _on_health_changed(new_health: int): + if new_health <= 0: + game_over() +``` + +## 🎮 游戏逻辑优化 + +### 状态机优化 +```gdscript +# StateMachine.gd +class_name StateMachine +extends Node + +var current_state: State +var states: Dictionary = {} + +func change_state(state_name: String): + if current_state: + current_state.exit() + + current_state = states[state_name] + current_state.enter() + +func _process(delta): + if current_state: + current_state.update(delta) +``` + +### AI优化 +```gdscript +# EnemyAI.gd +extends Node + +var update_interval: float = 0.2 # 每200ms更新一次AI +var update_timer: float = 0.0 + +func _process(delta): + update_timer += delta + if update_timer >= update_interval: + update_ai() + update_timer = 0.0 + +func update_ai(): + # AI逻辑更新 + # 路径寻找 + # 决策制定 + pass +``` + +### 碰撞检测优化 +```gdscript +# 使用空间分区优化碰撞检测 +class_name SpatialGrid +extends Node + +var grid_size: int = 64 +var grid: Dictionary = {} + +func add_object(obj: Node2D): + var grid_pos = Vector2( + int(obj.global_position.x / grid_size), + int(obj.global_position.y / grid_size) + ) + + if not grid.has(grid_pos): + grid[grid_pos] = [] + + grid[grid_pos].append(obj) + +func get_nearby_objects(pos: Vector2) -> Array: + var grid_pos = Vector2( + int(pos.x / grid_size), + int(pos.y / grid_size) + ) + + var nearby = [] + for x in range(-1, 2): + for y in range(-1, 2): + var check_pos = grid_pos + Vector2(x, y) + if grid.has(check_pos): + nearby.append_array(grid[check_pos]) + + return nearby +``` + +## 🌐 网络优化 + +### 数据压缩 +```gdscript +# NetworkManager.gd +func send_player_data(data: Dictionary): + # 只发送变化的数据 + var delta_data = get_changed_data(data) + + # 压缩数据 + var compressed = compress_data(delta_data) + + # 发送数据 + send_to_server(compressed) + +func compress_data(data: Dictionary) -> PackedByteArray: + var json_string = JSON.stringify(data) + var bytes = json_string.to_utf8_buffer() + return bytes.compress(FileAccess.COMPRESSION_GZIP) +``` + +### 批量更新 +```gdscript +# 批量发送网络更新 +var pending_updates: Array = [] +var update_timer: float = 0.0 + +func _process(delta): + update_timer += delta + if update_timer >= 0.05: # 每50ms发送一次 + if pending_updates.size() > 0: + send_batch_updates(pending_updates) + pending_updates.clear() + update_timer = 0.0 + +func queue_update(update_data: Dictionary): + pending_updates.append(update_data) +``` + +## 📊 性能监控 + +### FPS监控 +```gdscript +# FPSMonitor.gd +extends Control + +@onready var fps_label: Label = $FPSLabel +var fps_history: Array = [] + +func _process(delta): + var current_fps = Engine.get_frames_per_second() + fps_history.append(current_fps) + + if fps_history.size() > 60: # 保留60帧历史 + fps_history.pop_front() + + var avg_fps = 0 + for fps in fps_history: + avg_fps += fps + avg_fps /= fps_history.size() + + fps_label.text = "FPS: %d (平均: %d)" % [current_fps, avg_fps] + + # FPS过低时警告 + if avg_fps < 30: + modulate = Color.RED + elif avg_fps < 50: + modulate = Color.YELLOW + else: + modulate = Color.WHITE +``` + +### 性能分析器 +```gdscript +# Profiler.gd +class_name Profiler +extends Node + +var timers: Dictionary = {} + +func start_timer(name: String): + timers[name] = Time.get_time_dict_from_system() + +func end_timer(name: String) -> float: + if not timers.has(name): + return 0.0 + + var start_time = timers[name] + var end_time = Time.get_time_dict_from_system() + + var duration = (end_time.hour * 3600 + end_time.minute * 60 + end_time.second) - \ + (start_time.hour * 3600 + start_time.minute * 60 + start_time.second) + + timers.erase(name) + return duration + +# 使用示例 +func expensive_function(): + Profiler.start_timer("expensive_function") + + # 执行耗时操作 + for i in range(10000): + pass + + var duration = Profiler.end_timer("expensive_function") + print("函数执行时间: ", duration, "秒") +``` + +## 🛠️ 调试工具 + +### 性能调试面板 +```gdscript +# DebugPanel.gd +extends Control + +@onready var memory_label: Label = $VBox/MemoryLabel +@onready var fps_label: Label = $VBox/FPSLabel +@onready var objects_label: Label = $VBox/ObjectsLabel + +func _process(delta): + # 更新内存使用 + var memory = OS.get_static_memory_usage_by_type() + memory_label.text = "内存: %.1f MB" % (memory / 1024.0 / 1024.0) + + # 更新FPS + fps_label.text = "FPS: %d" % Engine.get_frames_per_second() + + # 更新对象数量 + var object_count = get_tree().get_node_count() + objects_label.text = "对象数: %d" % object_count +``` + +### 热点分析 +```gdscript +# HotspotAnalyzer.gd +extends Node + +var function_calls: Dictionary = {} +var function_times: Dictionary = {} + +func profile_function(func_name: String, callable: Callable): + var start_time = Time.get_time_dict_from_system() + + callable.call() + + var end_time = Time.get_time_dict_from_system() + var duration = (end_time.hour * 3600 + end_time.minute * 60 + end_time.second) - \ + (start_time.hour * 3600 + start_time.minute * 60 + start_time.second) + + if not function_calls.has(func_name): + function_calls[func_name] = 0 + function_times[func_name] = 0.0 + + function_calls[func_name] += 1 + function_times[func_name] += duration + +func print_profile_report(): + print("=== 性能分析报告 ===") + for func_name in function_calls.keys(): + var calls = function_calls[func_name] + var total_time = function_times[func_name] + var avg_time = total_time / calls + + print("%s: 调用%d次, 总时间%.3fs, 平均%.3fs" % [func_name, calls, total_time, avg_time]) +``` + +## 📚 最佳实践 + +### 开发阶段 +1. **早期优化** - 在设计阶段考虑性能 +2. **渐进开发** - 逐步添加功能并测试性能 +3. **定期测试** - 定期进行性能测试 +4. **文档记录** - 记录性能优化决策 + +### 测试阶段 +1. **多设备测试** - 在不同性能设备上测试 +2. **压力测试** - 测试极限情况下的性能 +3. **长时间测试** - 测试内存泄漏和性能衰减 +4. **用户测试** - 收集真实用户的性能反馈 + +### 发布阶段 +1. **性能监控** - 监控线上性能指标 +2. **快速响应** - 快速修复性能问题 +3. **持续优化** - 根据数据持续优化 +4. **版本对比** - 对比不同版本的性能表现 + +## 🔍 常见问题 + +### Q: 如何识别性能瓶颈? +A: 使用Godot的内置分析器,添加自定义性能监控,分析FPS和内存使用情况。 + +### Q: 内存使用过高怎么办? +A: 检查资源加载,使用对象池,及时释放不需要的对象,优化纹理大小。 + +### Q: 如何优化大量对象的渲染? +A: 使用MultiMesh批处理,实现视锥剔除,使用LOD系统,合并相同材质的对象。 + +### Q: 网络延迟如何优化? +A: 减少网络请求频率,压缩传输数据,使用预测和插值,实现客户端预测。 + +--- + +**记住:性能优化是一个持续的过程,需要在开发的各个阶段都保持关注!** \ No newline at end of file diff --git a/docs/scene_design.md b/docs/scene_design.md new file mode 100644 index 0000000..b6256c9 --- /dev/null +++ b/docs/scene_design.md @@ -0,0 +1,617 @@ +# 场景设计规范 + +本文档定义了 Whale Town 项目中场景设计的标准和最佳实践。 + +## 🎯 设计原则 + +### 核心原则 +1. **功能独立** - 每个场景都是独立的功能单元 +2. **职责单一** - 一个场景只负责一个主要功能 +3. **可复用性** - 场景组件应该能够在其他场景中复用 +4. **标准化** - 统一的场景结构和命名规范 +5. **性能优先** - 优化场景性能,避免不必要的资源消耗 + +### 场景分类 +- **主要场景** - 游戏的核心功能场景(主菜单、游戏场景、设置等) +- **UI场景** - 纯界面场景(对话框、HUD、菜单等) +- **游戏场景** - 包含游戏逻辑的场景(关卡、战斗、探索等) +- **工具场景** - 开发和测试用的场景 + +## 🏗️ 场景结构 + +### 标准目录结构 +``` +scenes/ +├── main_scene.tscn # 主场景 +├── auth_scene.tscn # 认证场景 +├── game_scene.tscn # 游戏场景 +├── settings_scene.tscn # 设置场景 +└── prefabs/ # 预制体组件 + ├── ui/ # UI组件 + │ ├── button.tscn + │ ├── dialog.tscn + │ └── menu.tscn + ├── characters/ # 角色组件 + │ ├── player.tscn + │ └── npc.tscn + ├── effects/ # 特效组件 + │ ├── particle_effect.tscn + │ └── animation_effect.tscn + └── items/ # 物品组件 + ├── collectible.tscn + └── interactive.tscn +``` + +### 场景命名规范 +- **主场景**: `scene_name.tscn` (snake_case) +- **预制体**: `component_name.tscn` (snake_case) +- **脚本文件**: `SceneName.gd` (PascalCase) +- **节点名称**: `NodeName` (PascalCase) 或 `nodeName` (camelCase) + +## 📝 场景设计模板 + +### 主场景结构模板 + +``` +SceneName (Control/Node2D) +├── Background (TextureRect/Sprite2D) # 背景 +├── UI (CanvasLayer) # UI层 +│ ├── HUD (Control) # 游戏HUD +│ ├── Menu (Control) # 菜单界面 +│ └── Dialog (Control) # 对话框 +├── Game (Node2D) # 游戏内容层 +│ ├── Player (CharacterBody2D) # 玩家 +│ ├── NPCs (Node2D) # NPC容器 +│ ├── Items (Node2D) # 物品容器 +│ └── Effects (Node2D) # 特效容器 +├── Audio (Node) # 音频管理 +│ ├── BGM (AudioStreamPlayer) # 背景音乐 +│ └── SFX (AudioStreamPlayer2D) # 音效 +└── Systems (Node) # 系统组件 + ├── CameraController (Node) # 相机控制 + ├── InputHandler (Node) # 输入处理 + └── StateManager (Node) # 状态管理 +``` + +### 场景脚本模板 + +```gdscript +# SceneName.gd +extends Control # 或 Node2D,根据场景类型选择 +class_name SceneName + +# 场景信息 +const SCENE_NAME = "SceneName" +const SCENE_VERSION = "1.0.0" + +# 场景状态 +enum SceneState { + LOADING, + READY, + ACTIVE, + PAUSED, + TRANSITIONING +} + +var current_state: SceneState = SceneState.LOADING + +# 节点引用 +@onready var background: TextureRect = $Background +@onready var ui_layer: CanvasLayer = $UI +@onready var game_layer: Node2D = $Game +@onready var audio_manager: Node = $Audio +@onready var systems: Node = $Systems + +# 场景数据 +var scene_data: Dictionary = {} +var is_initialized: bool = false + +# 信号定义 +signal scene_ready +signal scene_state_changed(old_state: SceneState, new_state: SceneState) +signal scene_data_updated(key: String, value) + +func _ready(): + """场景初始化""" + print("初始化场景: ", SCENE_NAME) + + # 初始化场景 + await initialize_scene() + + # 设置场景状态 + change_state(SceneState.READY) + + # 发送场景就绪信号 + scene_ready.emit() + +func initialize_scene(): + """初始化场景组件""" + # 加载场景数据 + await load_scene_data() + + # 初始化UI + initialize_ui() + + # 初始化游戏组件 + initialize_game_components() + + # 初始化音频 + initialize_audio() + + # 初始化系统 + initialize_systems() + + # 连接信号 + connect_signals() + + is_initialized = true + +func load_scene_data(): + """加载场景数据""" + # 从配置文件或网络加载场景数据 + var data_path = "res://data/scenes/%s.json" % SCENE_NAME.to_lower() + if FileAccess.file_exists(data_path): + var file = FileAccess.open(data_path, FileAccess.READ) + if file: + var json_string = file.get_as_text() + file.close() + + var json = JSON.new() + if json.parse(json_string) == OK: + scene_data = json.data + else: + print("警告: 场景数据JSON格式错误: ", data_path) + + # 等待一帧确保所有节点都已初始化 + await get_tree().process_frame + +func initialize_ui(): + """初始化UI组件""" + if ui_layer: + # 初始化UI组件 + for child in ui_layer.get_children(): + if child.has_method("initialize"): + child.initialize() + +func initialize_game_components(): + """初始化游戏组件""" + if game_layer: + # 初始化游戏组件 + for child in game_layer.get_children(): + if child.has_method("initialize"): + child.initialize() + +func initialize_audio(): + """初始化音频""" + if audio_manager: + # 设置背景音乐 + if scene_data.has("bgm"): + play_bgm(scene_data.bgm) + +func initialize_systems(): + """初始化系统组件""" + if systems: + # 初始化系统组件 + for child in systems.get_children(): + if child.has_method("initialize"): + child.initialize() + +func connect_signals(): + """连接信号""" + # 连接场景内部信号 + # 连接全局事件 + EventSystem.connect_event("game_paused", _on_game_paused) + EventSystem.connect_event("game_resumed", _on_game_resumed) + +func change_state(new_state: SceneState): + """改变场景状态""" + if current_state == new_state: + return + + var old_state = current_state + current_state = new_state + + print("场景状态变更: %s -> %s" % [SceneState.keys()[old_state], SceneState.keys()[new_state]]) + + # 处理状态变更 + _handle_state_change(old_state, new_state) + + # 发送状态变更信号 + scene_state_changed.emit(old_state, new_state) + +func _handle_state_change(old_state: SceneState, new_state: SceneState): + """处理状态变更""" + match new_state: + SceneState.LOADING: + _on_enter_loading_state() + SceneState.READY: + _on_enter_ready_state() + SceneState.ACTIVE: + _on_enter_active_state() + SceneState.PAUSED: + _on_enter_paused_state() + SceneState.TRANSITIONING: + _on_enter_transitioning_state() + +func _on_enter_loading_state(): + """进入加载状态""" + # 显示加载界面 + pass + +func _on_enter_ready_state(): + """进入就绪状态""" + # 场景准备完成 + pass + +func _on_enter_active_state(): + """进入活跃状态""" + # 开始游戏逻辑 + pass + +func _on_enter_paused_state(): + """进入暂停状态""" + # 暂停游戏逻辑 + get_tree().paused = true + +func _on_enter_transitioning_state(): + """进入转换状态""" + # 场景转换中 + pass + +func play_bgm(bgm_path: String): + """播放背景音乐""" + var bgm_player = audio_manager.get_node("BGM") as AudioStreamPlayer + if bgm_player and FileAccess.file_exists(bgm_path): + var audio_stream = load(bgm_path) + bgm_player.stream = audio_stream + bgm_player.play() + +func play_sfx(sfx_path: String, position: Vector2 = Vector2.ZERO): + """播放音效""" + var sfx_player = audio_manager.get_node("SFX") as AudioStreamPlayer2D + if sfx_player and FileAccess.file_exists(sfx_path): + var audio_stream = load(sfx_path) + sfx_player.stream = audio_stream + if position != Vector2.ZERO: + sfx_player.global_position = position + sfx_player.play() + +func update_scene_data(key: String, value): + """更新场景数据""" + scene_data[key] = value + scene_data_updated.emit(key, value) + +func get_scene_data(key: String, default_value = null): + """获取场景数据""" + return scene_data.get(key, default_value) + +func cleanup(): + """清理场景资源""" + print("清理场景: ", SCENE_NAME) + + # 断开信号连接 + EventSystem.disconnect_event("game_paused", _on_game_paused) + EventSystem.disconnect_event("game_resumed", _on_game_resumed) + + # 清理组件 + if ui_layer: + for child in ui_layer.get_children(): + if child.has_method("cleanup"): + child.cleanup() + + if game_layer: + for child in game_layer.get_children(): + if child.has_method("cleanup"): + child.cleanup() + + if systems: + for child in systems.get_children(): + if child.has_method("cleanup"): + child.cleanup() + + # 停止音频 + if audio_manager: + var bgm_player = audio_manager.get_node("BGM") as AudioStreamPlayer + if bgm_player: + bgm_player.stop() + +# 事件处理 +func _on_game_paused(): + """游戏暂停事件""" + if current_state == SceneState.ACTIVE: + change_state(SceneState.PAUSED) + +func _on_game_resumed(): + """游戏恢复事件""" + if current_state == SceneState.PAUSED: + change_state(SceneState.ACTIVE) + get_tree().paused = false + +# 输入处理 +func _input(event): + """处理输入事件""" + if current_state != SceneState.ACTIVE: + return + + # 处理场景特定的输入 + _handle_scene_input(event) + +func _handle_scene_input(event): + """处理场景特定输入""" + # 在子类中重写此方法 + pass + +# 生命周期方法 +func _process(delta): + """每帧更新""" + if current_state == SceneState.ACTIVE: + _update_scene(delta) + +func _update_scene(delta): + """更新场景逻辑""" + # 在子类中重写此方法 + pass + +func _physics_process(delta): + """物理更新""" + if current_state == SceneState.ACTIVE: + _physics_update_scene(delta) + +func _physics_update_scene(delta): + """物理更新场景逻辑""" + # 在子类中重写此方法 + pass +``` + +## 🎨 UI设计规范 + +### UI层级结构 +``` +UI (CanvasLayer) +├── Background (Control) # UI背景 +├── MainContent (Control) # 主要内容 +│ ├── Header (Control) # 头部区域 +│ ├── Body (Control) # 主体区域 +│ └── Footer (Control) # 底部区域 +├── Overlay (Control) # 覆盖层 +│ ├── Loading (Control) # 加载界面 +│ ├── Dialog (Control) # 对话框 +│ └── Toast (Control) # 提示消息 +└── Debug (Control) # 调试信息 +``` + +### UI组件规范 +1. **响应式设计** - 使用Anchor和Margin实现自适应布局 +2. **主题统一** - 使用统一的主题资源 +3. **动画效果** - 添加适当的过渡动画 +4. **无障碍支持** - 考虑键盘导航和屏幕阅读器 + +### UI脚本模板 +```gdscript +# UIComponent.gd +extends Control +class_name UIComponent + +signal ui_action(action_name: String, data: Dictionary) + +@export var auto_initialize: bool = true +@export var animation_duration: float = 0.3 + +var is_visible: bool = false +var is_initialized: bool = false + +func _ready(): + if auto_initialize: + initialize() + +func initialize(): + """初始化UI组件""" + if is_initialized: + return + + # 设置初始状态 + modulate.a = 0.0 + visible = false + + # 连接信号 + _connect_signals() + + is_initialized = true + +func show_ui(animated: bool = true): + """显示UI""" + if is_visible: + return + + visible = true + is_visible = true + + if animated: + var tween = create_tween() + tween.tween_property(self, "modulate:a", 1.0, animation_duration) + else: + modulate.a = 1.0 + +func hide_ui(animated: bool = true): + """隐藏UI""" + if not is_visible: + return + + is_visible = false + + if animated: + var tween = create_tween() + tween.tween_property(self, "modulate:a", 0.0, animation_duration) + await tween.finished + visible = false + else: + modulate.a = 0.0 + visible = false + +func _connect_signals(): + """连接信号""" + # 在子类中重写 + pass +``` + +## 🎮 游戏场景规范 + +### 游戏场景结构 +``` +GameScene (Node2D) +├── Background (ParallaxBackground) # 背景层 +├── Environment (Node2D) # 环境层 +│ ├── Terrain (TileMap) # 地形 +│ ├── Props (Node2D) # 道具 +│ └── Decorations (Node2D) # 装饰 +├── Entities (Node2D) # 实体层 +│ ├── Player (CharacterBody2D) # 玩家 +│ ├── NPCs (Node2D) # NPC +│ ├── Enemies (Node2D) # 敌人 +│ └── Items (Node2D) # 物品 +├── Effects (Node2D) # 特效层 +│ ├── Particles (Node2D) # 粒子效果 +│ └── Animations (Node2D) # 动画效果 +└── Camera (Camera2D) # 相机 +``` + +### 性能优化 +1. **对象池** - 重用频繁创建销毁的对象 +2. **视锥剔除** - 只渲染可见区域的对象 +3. **LOD系统** - 根据距离调整细节级别 +4. **批量处理** - 合并相似的渲染调用 + +## 🔧 场景管理 + +### 场景切换 +```gdscript +# 使用SceneManager进行场景切换 +SceneManager.change_scene("game_scene", { + "level": 1, + "player_data": player_data +}) + +# 带过渡效果的场景切换 +SceneManager.change_scene_with_transition("battle_scene", { + "enemy_data": enemy_data +}, "fade") +``` + +### 场景数据传递 +```gdscript +# 发送场景数据 +var scene_data = { + "player_level": 10, + "inventory": player_inventory, + "quest_progress": quest_data +} +SceneManager.change_scene("next_scene", scene_data) + +# 接收场景数据 +func _on_scene_data_received(data: Dictionary): + if data.has("player_level"): + player_level = data.player_level + if data.has("inventory"): + load_inventory(data.inventory) +``` + +## 🧪 场景测试 + +### 测试场景创建 +```gdscript +# TestScene.gd +extends "res://scenes/BaseScene.gd" + +func _ready(): + super._ready() + + # 设置测试环境 + setup_test_environment() + + # 运行测试用例 + run_test_cases() + +func setup_test_environment(): + """设置测试环境""" + # 创建测试数据 + # 设置测试状态 + pass + +func run_test_cases(): + """运行测试用例""" + test_scene_initialization() + test_ui_interactions() + test_game_logic() + +func test_scene_initialization(): + """测试场景初始化""" + assert(is_initialized, "场景应该已初始化") + assert(current_state == SceneState.READY, "场景状态应该为READY") + +func test_ui_interactions(): + """测试UI交互""" + # 模拟用户输入 + # 验证UI响应 + pass + +func test_game_logic(): + """测试游戏逻辑""" + # 测试游戏规则 + # 验证状态变化 + pass +``` + +### 性能测试 +```gdscript +# 性能监控 +func _process(delta): + super._process(delta) + + # 监控帧率 + var fps = Engine.get_frames_per_second() + if fps < 30: + print("警告: 帧率过低: ", fps) + + # 监控内存使用 + var memory_usage = OS.get_static_memory_usage_by_type() + if memory_usage > 100 * 1024 * 1024: # 100MB + print("警告: 内存使用过高: ", memory_usage / 1024 / 1024, "MB") +``` + +## 📚 最佳实践 + +### 代码组织 +1. **单一职责** - 每个场景只负责一个主要功能 +2. **模块化** - 将复杂功能拆分为独立组件 +3. **可测试** - 设计易于测试的场景结构 +4. **文档化** - 为场景添加详细的文档说明 + +### 性能优化 +1. **延迟加载** - 按需加载资源和组件 +2. **对象复用** - 使用对象池管理频繁创建的对象 +3. **批量操作** - 合并相似的操作减少开销 +4. **内存管理** - 及时释放不需要的资源 + +### 用户体验 +1. **响应式设计** - 支持不同分辨率和设备 +2. **流畅动画** - 添加适当的过渡效果 +3. **错误处理** - 优雅处理异常情况 +4. **加载提示** - 为长时间操作提供进度反馈 + +## 🔍 常见问题 + +### Q: 如何处理场景间的数据传递? +A: 使用SceneManager的场景切换方法传递数据,或通过全局单例存储共享数据。 + +### Q: 场景性能如何优化? +A: 使用对象池、视锥剔除、LOD系统,避免在_process中执行重复计算。 + +### Q: 如何调试场景问题? +A: 使用Godot的远程调试器,添加性能监控,编写场景测试用例。 + +### Q: 场景切换如何添加过渡效果? +A: 使用SceneManager的过渡系统,或自定义Tween动画实现过渡效果。 + +--- + +**记住:良好的场景设计是游戏体验的基础!** \ No newline at end of file