forked from datawhale/whale-town-front
docs: 重新组织文档结构,按开发阶段分类
新的目录结构: 01-项目入门/ # 新人必读,项目基础 02-开发规范/ # 编码标准和规范 03-技术实现/ # 具体开发指导 04-高级开发/ # 进阶开发技巧 05-部署运维/ # 发布和部署 06-功能模块/ # 特定功能文档 新增导航文档: - docs/README.md - 完整的文档导航和使用指南 - 各目录下的README.md - 分类说明和使用指导 优化效果: - 开发者可以按阶段快速定位需要的文档 - 新人有清晰的学习路径 - 不同角色有针对性的文档推荐 - 提供了问题导向的快速查找功能
This commit is contained in:
381
docs/02-开发规范/Git提交规范.md
Normal file
381
docs/02-开发规范/Git提交规范.md
Normal file
@@ -0,0 +1,381 @@
|
||||
# Git 提交规范
|
||||
|
||||
本文档定义了项目的 Git 提交信息格式规范,以保持提交历史的清晰和一致性。
|
||||
|
||||
## 提交格式
|
||||
|
||||
```
|
||||
<类型>:<简短描述>
|
||||
|
||||
[可选的详细描述]
|
||||
|
||||
[可选的注释或关联 Issue]
|
||||
```
|
||||
|
||||
## 提交类型
|
||||
|
||||
### 主要类型
|
||||
|
||||
| 类型 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `init` | 项目初始化 | `init:项目初始化,搭建Godot文件结构` |
|
||||
| `feat` | 新增功能 | `feat:添加角色移动系统` |
|
||||
| `fix` | 修复 Bug | `fix:修复角色跳跃时的碰撞检测问题` |
|
||||
| `docs` | 文档更新 | `docs:更新 README 中的安装说明` |
|
||||
| `style` | 代码格式调整(不影响功能) | `style:统一代码缩进格式` |
|
||||
| `refactor` | 代码重构(不新增功能也不修复 Bug) | `refactor:重构敌人 AI 逻辑` |
|
||||
| `perf` | 性能优化 | `perf:优化场景加载速度` |
|
||||
| `test` | 添加或修改测试 | `test:添加角色控制器单元测试` |
|
||||
| `chore` | 构建过程或辅助工具的变动 | `chore:更新 .gitignore 文件` |
|
||||
| `revert` | 回滚之前的提交 | `revert:回滚 feat:添加角色移动系统` |
|
||||
|
||||
### 场景和资源相关
|
||||
|
||||
| 类型 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `scene` | 场景文件相关 | `scene:创建主菜单场景` |
|
||||
| `asset` | 资源文件相关 | `asset:添加角色精灵图和动画` |
|
||||
| `ui` | UI 界面相关 | `ui:设计游戏暂停菜单` |
|
||||
| `audio` | 音频相关 | `audio:添加背景音乐和音效` |
|
||||
|
||||
### 游戏开发特定类型
|
||||
|
||||
| 类型 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `gameplay` | 游戏玩法相关 | `gameplay:实现敌人生成机制` |
|
||||
| `level` | 关卡设计相关 | `level:完成第一关卡设计` |
|
||||
| `config` | 配置文件相关 | `config:调整游戏难度参数` |
|
||||
| `plugin` | 插件相关 | `plugin:集成对话系统插件` |
|
||||
|
||||
## 提交示例
|
||||
|
||||
### 基础示例
|
||||
|
||||
```bash
|
||||
# 项目初始化
|
||||
git commit -m "init:项目初始化,搭建Godot文件结构"
|
||||
|
||||
# 新增功能
|
||||
git commit -m "feat:实现玩家角色的移动和跳跃"
|
||||
|
||||
# 修复问题
|
||||
git commit -m "fix:修复敌人穿墙的碰撞问题"
|
||||
|
||||
# 文档更新
|
||||
git commit -m "docs:添加 Git 提交规范文档"
|
||||
```
|
||||
|
||||
### 带详细描述的示例
|
||||
|
||||
```bash
|
||||
git commit -m "feat:添加存档系统
|
||||
|
||||
- 实现游戏进度保存功能
|
||||
- 支持多个存档槽位
|
||||
- 添加自动保存机制
|
||||
|
||||
关联 Issue #12"
|
||||
```
|
||||
|
||||
### 场景和资源示例
|
||||
|
||||
```bash
|
||||
# 场景相关
|
||||
git commit -m "scene:创建战斗场景并配置相机"
|
||||
|
||||
# 资源相关
|
||||
git commit -m "asset:导入角色动画资源包"
|
||||
|
||||
# UI 相关
|
||||
git commit -m "ui:完成主菜单界面设计"
|
||||
|
||||
# 音频相关
|
||||
git commit -m "audio:添加脚步声和跳跃音效"
|
||||
```
|
||||
|
||||
### 游戏开发示例
|
||||
|
||||
```bash
|
||||
# 游戏玩法
|
||||
git commit -m "gameplay:实现道具拾取和使用系统"
|
||||
|
||||
# 关卡设计
|
||||
git commit -m "level:设计并实现第三关卡"
|
||||
|
||||
# 配置调整
|
||||
git commit -m "config:平衡敌人血量和伤害数值"
|
||||
|
||||
# 插件集成
|
||||
git commit -m "plugin:添加粒子效果插件"
|
||||
```
|
||||
|
||||
### 重构和优化示例
|
||||
|
||||
```bash
|
||||
# 代码重构
|
||||
git commit -m "refactor:重构角色状态机逻辑"
|
||||
|
||||
# 性能优化
|
||||
git commit -m "perf:优化大量敌人时的渲染性能"
|
||||
|
||||
# 代码格式
|
||||
git commit -m "style:统一 GDScript 代码风格"
|
||||
```
|
||||
|
||||
## 多类型改动的处理
|
||||
|
||||
### 问题场景
|
||||
|
||||
有时一次开发工作可能同时包含多种类型的改动,例如:
|
||||
- 既修复了 Bug,又添加了新功能
|
||||
- 既重构了代码,又优化了性能
|
||||
- 既更新了场景,又修改了脚本
|
||||
|
||||
### 推荐做法:拆分提交(强烈推荐)⭐
|
||||
|
||||
**原则**:一次提交只做一件事,保持提交历史清晰
|
||||
|
||||
```bash
|
||||
# ❌ 不推荐:混合提交
|
||||
git commit -m "fix + feat:修复跳跃 Bug 并添加二段跳功能"
|
||||
|
||||
# ✅ 推荐:拆分成两次提交
|
||||
git add PlayerController.gd # 只添加修复 Bug 的部分
|
||||
git commit -m "fix:修复角色跳跃时的碰撞检测问题"
|
||||
|
||||
git add PlayerController.gd # 添加新功能的部分
|
||||
git commit -m "feat:实现角色二段跳功能"
|
||||
```
|
||||
|
||||
### 拆分提交的优势
|
||||
|
||||
1. **清晰的历史记录**:每次提交目的明确,便于回溯
|
||||
2. **便于代码审查**:审查者可以分别查看不同类型的改动
|
||||
3. **灵活的回滚**:可以单独回滚某个功能或修复,而不影响其他改动
|
||||
4. **更好的协作**:团队成员能快速理解每次提交的意图
|
||||
5. **自动化工具友好**:CI/CD 和版本管理工具能更好地处理
|
||||
|
||||
### 如何拆分提交
|
||||
|
||||
#### 方法一:使用 git add -p(交互式暂存)
|
||||
|
||||
```bash
|
||||
# 交互式选择要暂存的代码块
|
||||
git add -p PlayerController.gd
|
||||
|
||||
# 选择修复 Bug 的部分,提交
|
||||
git commit -m "fix:修复角色跳跃时的碰撞检测问题"
|
||||
|
||||
# 暂存剩余的新功能代码
|
||||
git add PlayerController.gd
|
||||
git commit -m "feat:实现角色二段跳功能"
|
||||
```
|
||||
|
||||
#### 方法二:分步开发和提交
|
||||
|
||||
```bash
|
||||
# 第一步:先完成并提交 Bug 修复
|
||||
# 修改代码...
|
||||
git add PlayerController.gd
|
||||
git commit -m "fix:修复角色跳跃时的碰撞检测问题"
|
||||
|
||||
# 第二步:再开发并提交新功能
|
||||
# 继续修改代码...
|
||||
git add PlayerController.gd
|
||||
git commit -m "feat:实现角色二段跳功能"
|
||||
```
|
||||
|
||||
#### 方法三:使用临时分支
|
||||
|
||||
```bash
|
||||
# 创建临时分支保存当前工作
|
||||
git stash
|
||||
|
||||
# 只恢复修复 Bug 的部分并提交
|
||||
git stash pop
|
||||
# 手动撤销新功能代码,只保留 Bug 修复
|
||||
git add PlayerController.gd
|
||||
git commit -m "fix:修复角色跳跃时的碰撞检测问题"
|
||||
|
||||
# 恢复新功能代码并提交
|
||||
# 重新添加新功能代码...
|
||||
git add PlayerController.gd
|
||||
git commit -m "feat:实现角色二段跳功能"
|
||||
```
|
||||
|
||||
### 特殊情况:确实无法拆分时
|
||||
|
||||
如果改动确实紧密耦合、无法拆分(这种情况应该很少见),可以使用以下方式:
|
||||
|
||||
#### 方式一:选择主要类型 + 详细描述(推荐)
|
||||
|
||||
```bash
|
||||
git commit -m "feat:实现角色二段跳功能
|
||||
|
||||
- 添加二段跳的核心逻辑
|
||||
- 修复原有跳跃系统的碰撞检测问题
|
||||
- 优化跳跃动画的过渡效果
|
||||
|
||||
本次提交包含了功能开发和 Bug 修复,因为二段跳的实现
|
||||
依赖于修复原有跳跃系统的碰撞问题。"
|
||||
```
|
||||
|
||||
#### 方式二:使用多行描述分别说明
|
||||
|
||||
```bash
|
||||
git commit -m "feat:重构并优化角色移动系统
|
||||
|
||||
功能改进:
|
||||
- 实现新的移动状态机
|
||||
- 添加冲刺功能
|
||||
|
||||
Bug 修复:
|
||||
- 修复移动时的抖动问题
|
||||
- 修复斜坡上的滑动问题
|
||||
|
||||
性能优化:
|
||||
- 减少物理计算频率
|
||||
- 优化碰撞检测算法"
|
||||
```
|
||||
|
||||
### 什么时候应该拆分?
|
||||
|
||||
| 情况 | 是否拆分 | 原因 |
|
||||
|------|---------|------|
|
||||
| 修复 Bug + 添加新功能 | ✅ 应该拆分 | 两个独立的逻辑改动 |
|
||||
| 重构代码 + 性能优化 | ✅ 应该拆分 | 目的不同,影响范围不同 |
|
||||
| 添加场景 + 编写脚本 | ✅ 应该拆分 | 不同类型的文件改动 |
|
||||
| 修复 Bug + 添加该 Bug 的测试 | ❌ 可以合并 | 测试是修复的一部分 |
|
||||
| 重构 + 重构后必需的修复 | ❌ 可以合并 | 修复是重构的直接结果 |
|
||||
| 添加功能 + 更新相关文档 | ⚠️ 视情况而定 | 简单文档可合并,复杂文档应拆分 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **使用中文冒号**:类型后使用中文冒号 `:` 而非英文冒号 `:`
|
||||
2. **简短明确**:描述应简洁明了,一般不超过 50 个字符
|
||||
3. **动词开头**:描述部分使用动词开头,如"添加"、"修复"、"更新"等
|
||||
4. **一次提交一个改动**:每次提交应该只包含一个逻辑改动(最重要的原则)⭐
|
||||
5. **详细描述**:对于复杂的改动,应该添加详细描述说明改动的原因和影响
|
||||
6. **避免混合类型**:不要在一次提交中混合多种类型的改动(如 fix + feat)
|
||||
7. **先提交修复,再提交功能**:如果必须在同一开发周期内完成,优先提交 Bug 修复
|
||||
8. **使用 git add -p**:善用交互式暂存来精确控制每次提交的内容
|
||||
9. **提交前自查**:问自己"这次提交是否只做了一件事?"
|
||||
10. **保持提交原子性**:每次提交都应该是完整的、可独立运行的改动
|
||||
|
||||
## 分支命名规范
|
||||
|
||||
```bash
|
||||
# 功能分支
|
||||
feature/角色系统
|
||||
feature/存档功能
|
||||
|
||||
# 修复分支
|
||||
fix/碰撞检测
|
||||
fix/音频播放
|
||||
|
||||
# 开发分支
|
||||
dev
|
||||
develop
|
||||
|
||||
# 发布分支
|
||||
release/v1.0.0
|
||||
release/v1.1.0
|
||||
```
|
||||
|
||||
## 常见问题 FAQ
|
||||
|
||||
### Q1: 我同时修复了 3 个小 Bug,应该分 3 次提交吗?
|
||||
|
||||
**A**: 视情况而定:
|
||||
- 如果是**相关的 Bug**(同一个系统/模块),可以合并为一次提交
|
||||
- 如果是**不相关的 Bug**(不同系统/模块),应该分别提交
|
||||
|
||||
```bash
|
||||
# ✅ 相关 Bug 可以合并
|
||||
git commit -m "fix:修复角色移动系统的多个问题
|
||||
|
||||
- 修复在斜坡上的滑动问题
|
||||
- 修复跳跃时的速度计算错误
|
||||
- 修复冲刺时的方向判断问题"
|
||||
|
||||
# ✅ 不相关 Bug 应该拆分
|
||||
git commit -m "fix:修复角色跳跃时的碰撞检测问题"
|
||||
git commit -m "fix:修复 UI 菜单的按钮响应延迟"
|
||||
git commit -m "fix:修复音频播放时的内存泄漏"
|
||||
```
|
||||
|
||||
### Q2: 我在开发新功能时发现了 Bug,应该怎么办?
|
||||
|
||||
**A**: 推荐流程:
|
||||
1. 暂存当前新功能的代码(`git stash`)
|
||||
2. 修复 Bug 并提交
|
||||
3. 恢复新功能代码继续开发
|
||||
4. 完成后提交新功能
|
||||
|
||||
```bash
|
||||
# 保存当前工作
|
||||
git stash save "WIP: 开发二段跳功能"
|
||||
|
||||
# 修复 Bug
|
||||
git add PlayerController.gd
|
||||
git commit -m "fix:修复角色跳跃时的碰撞检测问题"
|
||||
|
||||
# 恢复工作并继续开发
|
||||
git stash pop
|
||||
# 继续开发...
|
||||
git commit -m "feat:实现角色二段跳功能"
|
||||
```
|
||||
|
||||
### Q3: 重构代码时顺便优化了性能,算一次提交吗?
|
||||
|
||||
**A**: 应该拆分:
|
||||
- 重构(`refactor`):改变代码结构但不改变行为
|
||||
- 优化(`perf`):改善性能表现
|
||||
|
||||
```bash
|
||||
# ✅ 拆分提交
|
||||
git commit -m "refactor:重构敌人 AI 状态机逻辑"
|
||||
git commit -m "perf:优化敌人 AI 的路径计算性能"
|
||||
```
|
||||
|
||||
### Q4: 我应该多久提交一次?
|
||||
|
||||
**A**: 遵循以下原则:
|
||||
- ✅ 完成一个**完整的逻辑单元**就提交
|
||||
- ✅ 代码能够**正常运行**时提交
|
||||
- ✅ 下班前提交当天的工作进度
|
||||
- ❌ 不要等到功能完全完成才提交(可能跨越多天)
|
||||
- ❌ 不要提交无法运行的代码
|
||||
|
||||
### Q5: 提交信息写错了怎么办?
|
||||
|
||||
**A**: 使用 `git commit --amend` 修改最后一次提交:
|
||||
|
||||
```bash
|
||||
# 修改最后一次提交信息
|
||||
git commit --amend -m "fix:修复角色跳跃时的碰撞检测问题"
|
||||
|
||||
# 如果已经推送到远程,需要强制推送(谨慎使用)
|
||||
git push --force-with-lease
|
||||
```
|
||||
|
||||
⚠️ **注意**:只修改未推送或未被他人使用的提交!
|
||||
|
||||
## 工具推荐
|
||||
|
||||
- **Commitizen**: 交互式提交信息生成工具
|
||||
- **Git Hooks**: 使用 pre-commit 钩子自动检查提交格式
|
||||
- **Conventional Commits**: 遵循约定式提交规范
|
||||
- **Git GUI 工具**: GitKraken、SourceTree 等支持可视化暂存部分代码
|
||||
|
||||
## 总结
|
||||
|
||||
记住这些核心原则:
|
||||
|
||||
1. ⭐ **一次提交只做一件事** - 最重要的原则
|
||||
2. 🔀 **能拆分就拆分** - 保持提交历史清晰
|
||||
3. 🎯 **提交要有意义** - 每次提交都应该是完整的改动
|
||||
4. 📝 **描述要清晰** - 让别人(和未来的自己)能快速理解
|
||||
5. 🚫 **避免混合类型** - 不要 fix + feat 混在一起
|
||||
|
||||
**好的提交习惯 = 清晰的项目历史 = 高效的团队协作**
|
||||
105
docs/02-开发规范/README.md
Normal file
105
docs/02-开发规范/README.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# 📋 开发规范
|
||||
|
||||
> **适用人群**: 所有开发者
|
||||
> **使用时机**: 编码过程中,代码审查时
|
||||
|
||||
这个目录包含了项目的所有编码标准和开发规范,确保团队代码风格一致,架构设计统一。
|
||||
|
||||
## 📚 规范文档
|
||||
|
||||
### 基础规范 📝
|
||||
**[命名规范.md](命名规范.md)**
|
||||
- 文件、类、变量、函数命名标准
|
||||
- 资源文件命名规则
|
||||
- 目录结构命名约定
|
||||
|
||||
**[代码注释规范.md](代码注释规范.md)**
|
||||
- 注释格式和标准
|
||||
- 文档生成规范
|
||||
- AI辅助开发指南
|
||||
|
||||
**[Git提交规范.md](Git提交规范.md)**
|
||||
- 提交信息格式标准
|
||||
- 分支管理策略
|
||||
- 代码审查流程
|
||||
|
||||
### 架构规范 🏗️
|
||||
**[架构与通信规范.md](架构与通信规范.md)**
|
||||
- 分层架构设计原则
|
||||
- EventSystem事件系统使用
|
||||
- 组件间通信标准
|
||||
- 单例管理规范
|
||||
|
||||
### 质量标准 ⭐
|
||||
**[开发哲学与最佳实践.md](开发哲学与最佳实践.md)**
|
||||
- 项目开发理念
|
||||
- 代码质量标准
|
||||
- 最佳实践指导
|
||||
- 代码审查清单
|
||||
|
||||
## 🎯 使用指南
|
||||
|
||||
### 新人学习路径
|
||||
1. **命名规范** - 学会正确命名
|
||||
2. **架构与通信规范** - 理解项目架构
|
||||
3. **开发哲学与最佳实践** - 掌握质量标准
|
||||
4. **代码注释规范** - 学会写好注释
|
||||
5. **Git提交规范** - 规范版本控制
|
||||
|
||||
### 日常开发参考
|
||||
- 编码时参考 **命名规范** 和 **架构规范**
|
||||
- 提交代码前检查 **最佳实践** 清单
|
||||
- 写注释时遵循 **注释规范**
|
||||
- 提交时遵循 **Git规范**
|
||||
|
||||
### 代码审查要点
|
||||
- [ ] 命名是否符合规范
|
||||
- [ ] 架构设计是否合理
|
||||
- [ ] 代码质量是否达标
|
||||
- [ ] 注释是否完整清晰
|
||||
- [ ] 提交信息是否规范
|
||||
|
||||
## ⚠️ 重要提醒
|
||||
|
||||
### 强制性规范
|
||||
以下规范是**强制性**的,必须严格遵守:
|
||||
- 文件和目录命名规范
|
||||
- EventSystem通信规范
|
||||
- 类型安全要求
|
||||
- Git提交格式
|
||||
|
||||
### 建议性规范
|
||||
以下规范是**建议性**的,推荐遵循:
|
||||
- 代码注释的详细程度
|
||||
- 函数长度和复杂度
|
||||
- 性能优化建议
|
||||
|
||||
## 🔄 规范更新
|
||||
|
||||
### 更新原则
|
||||
- 规范变更需要团队讨论
|
||||
- 重大变更需要文档化说明
|
||||
- 保持向后兼容性
|
||||
|
||||
### 更新流程
|
||||
1. 提出规范变更建议
|
||||
2. 团队讨论和评审
|
||||
3. 更新相关文档
|
||||
4. 通知所有开发者
|
||||
|
||||
## 🤝 团队协作
|
||||
|
||||
### 规范执行
|
||||
- 代码审查时严格检查规范遵循情况
|
||||
- 定期进行规范培训和分享
|
||||
- 鼓励团队成员提出改进建议
|
||||
|
||||
### 问题反馈
|
||||
如果发现规范问题或有改进建议:
|
||||
- 创建Issue讨论
|
||||
- 在团队会议中提出
|
||||
- 通过PR提交改进方案
|
||||
|
||||
---
|
||||
|
||||
**记住:规范不是束缚,而是团队协作的基础!**
|
||||
433
docs/02-开发规范/代码注释规范.md
Normal file
433
docs/02-开发规范/代码注释规范.md
Normal file
@@ -0,0 +1,433 @@
|
||||
# GDScript 代码注释规范
|
||||
|
||||
本文档详细说明了 whaleTown 项目中 GDScript 代码的注释规范,旨在提高代码可读性和维护性。
|
||||
|
||||
## 目录
|
||||
|
||||
- [基本原则](#基本原则)
|
||||
- [文件头注释](#文件头注释)
|
||||
- [类和函数注释](#类和函数注释)
|
||||
- [变量注释](#变量注释)
|
||||
- [行内注释](#行内注释)
|
||||
- [特殊注释标记](#特殊注释标记)
|
||||
- [AI辅助注释指南](#ai辅助注释指南)
|
||||
|
||||
## 基本原则
|
||||
|
||||
### 注释的目的
|
||||
- **解释为什么**,而不是解释做什么
|
||||
- **提供上下文**,帮助理解代码意图
|
||||
- **记录重要决策**和设计考虑
|
||||
- **警告潜在问题**和注意事项
|
||||
|
||||
### 注释质量标准
|
||||
- 简洁明了,避免冗余
|
||||
- 使用中文,保持一致性
|
||||
- 及时更新,与代码同步
|
||||
- 避免显而易见的注释
|
||||
|
||||
## 文件头注释
|
||||
|
||||
每个 GDScript 文件都应该包含文件头注释,说明文件的用途和基本信息。
|
||||
|
||||
### 标准格式
|
||||
|
||||
```gdscript
|
||||
# ============================================================================
|
||||
# 文件名: PlayerController.gd
|
||||
# 作用: 玩家角色控制器,处理玩家输入和移动逻辑
|
||||
#
|
||||
# 主要功能:
|
||||
# - 处理键盘和手柄输入
|
||||
# - 控制角色移动和跳跃
|
||||
# - 管理角色状态切换
|
||||
# - 处理碰撞检测
|
||||
#
|
||||
# 依赖: MovementComponent, AnimationComponent
|
||||
# 作者: [开发者名称]
|
||||
# 创建时间: 2024-12-24
|
||||
# ============================================================================
|
||||
|
||||
extends CharacterBody2D
|
||||
```
|
||||
|
||||
### 管理器类文件头
|
||||
|
||||
```gdscript
|
||||
# ============================================================================
|
||||
# 游戏管理器 - GameManager.gd
|
||||
#
|
||||
# 全局单例管理器,负责游戏状态管理和生命周期控制
|
||||
#
|
||||
# 核心职责:
|
||||
# - 游戏状态切换 (加载、认证、游戏中、暂停等)
|
||||
# - 用户信息管理
|
||||
# - 全局配置访问
|
||||
# - 系统初始化和清理
|
||||
#
|
||||
# 使用方式:
|
||||
# GameManager.change_state(GameManager.GameState.IN_GAME)
|
||||
# GameManager.set_current_user("player123")
|
||||
#
|
||||
# 注意事项:
|
||||
# - 作为自动加载单例,全局可访问
|
||||
# - 状态变更会触发 game_state_changed 信号
|
||||
# ============================================================================
|
||||
|
||||
extends Node
|
||||
```
|
||||
## 类和函数注释
|
||||
|
||||
### 类注释
|
||||
|
||||
```gdscript
|
||||
# 玩家数据类
|
||||
#
|
||||
# 存储和管理玩家的基本属性和状态信息
|
||||
# 支持数据序列化和反序列化,用于存档系统
|
||||
class_name PlayerData
|
||||
|
||||
# 武器组件类
|
||||
#
|
||||
# 为角色提供武器功能,包括攻击、换弹、特殊技能等
|
||||
# 可以挂载到任何具有攻击能力的角色上
|
||||
#
|
||||
# 使用示例:
|
||||
# var weapon = WeaponComponent.new()
|
||||
# weapon.setup_weapon("sword", 50)
|
||||
# add_child(weapon)
|
||||
class_name WeaponComponent
|
||||
```
|
||||
|
||||
### 函数注释格式
|
||||
|
||||
```gdscript
|
||||
# 处理玩家输入并更新移动状态
|
||||
#
|
||||
# 参数:
|
||||
# delta: float - 帧时间间隔
|
||||
#
|
||||
# 返回值: 无
|
||||
#
|
||||
# 注意事项:
|
||||
# - 需要在 _physics_process 中调用
|
||||
# - 会自动处理重力和碰撞
|
||||
func handle_movement(delta: float) -> void:
|
||||
|
||||
# 验证用户输入的邮箱格式
|
||||
#
|
||||
# 参数:
|
||||
# email: String - 待验证的邮箱地址
|
||||
#
|
||||
# 返回值:
|
||||
# Dictionary - 包含验证结果和错误信息
|
||||
# {
|
||||
# "valid": bool, # 是否有效
|
||||
# "message": String # 错误信息(如果无效)
|
||||
# }
|
||||
#
|
||||
# 使用示例:
|
||||
# var result = validate_email("test@example.com")
|
||||
# if result.valid:
|
||||
# print("邮箱格式正确")
|
||||
static func validate_email(email: String) -> Dictionary:
|
||||
```
|
||||
## 变量注释
|
||||
|
||||
### 成员变量注释
|
||||
|
||||
```gdscript
|
||||
# 玩家基础属性
|
||||
@export var max_health: int = 100 # 最大生命值
|
||||
@export var move_speed: float = 200.0 # 移动速度 (像素/秒)
|
||||
@export var jump_force: float = -400.0 # 跳跃力度 (负值向上)
|
||||
|
||||
# 状态管理
|
||||
var current_health: int # 当前生命值
|
||||
var is_grounded: bool = false # 是否在地面上
|
||||
var can_double_jump: bool = true # 是否可以二段跳
|
||||
|
||||
# 节点引用 - 在 _ready() 中初始化
|
||||
@onready var sprite: Sprite2D = $Sprite2D
|
||||
@onready var collision: CollisionShape2D = $CollisionShape2D
|
||||
@onready var animation_player: AnimationPlayer = $AnimationPlayer
|
||||
|
||||
# 私有变量 - 内部使用
|
||||
var _velocity: Vector2 = Vector2.ZERO # 当前速度向量
|
||||
var _last_direction: int = 1 # 最后面向方向 (1=右, -1=左)
|
||||
```
|
||||
|
||||
### 常量注释
|
||||
|
||||
```gdscript
|
||||
# 游戏配置常量
|
||||
const GRAVITY: float = 980.0 # 重力加速度 (像素/秒²)
|
||||
const MAX_FALL_SPEED: float = 1000.0 # 最大下落速度
|
||||
const COYOTE_TIME: float = 0.1 # 土狼时间 (离开平台后仍可跳跃的时间)
|
||||
|
||||
# 输入动作名称
|
||||
const ACTION_MOVE_LEFT: String = "move_left"
|
||||
const ACTION_MOVE_RIGHT: String = "move_right"
|
||||
const ACTION_JUMP: String = "jump"
|
||||
|
||||
# 动画名称常量
|
||||
const ANIM_IDLE: String = "idle"
|
||||
const ANIM_WALK: String = "walk"
|
||||
const ANIM_JUMP: String = "jump"
|
||||
const ANIM_FALL: String = "fall"
|
||||
```
|
||||
|
||||
## 行内注释
|
||||
|
||||
### 复杂逻辑注释
|
||||
|
||||
```gdscript
|
||||
func update_movement(delta: float):
|
||||
# 处理水平移动输入
|
||||
var input_direction = Input.get_axis("move_left", "move_right")
|
||||
|
||||
if input_direction != 0:
|
||||
velocity.x = input_direction * move_speed
|
||||
_last_direction = sign(input_direction) # 记录面向方向
|
||||
else:
|
||||
# 应用摩擦力,逐渐停止
|
||||
velocity.x = move_toward(velocity.x, 0, friction * delta)
|
||||
|
||||
# 应用重力 (只在空中时)
|
||||
if not is_on_floor():
|
||||
velocity.y += gravity * delta
|
||||
velocity.y = min(velocity.y, max_fall_speed) # 限制最大下落速度
|
||||
|
||||
# 处理跳跃输入
|
||||
if Input.is_action_just_pressed("jump"):
|
||||
if is_on_floor():
|
||||
velocity.y = jump_force # 普通跳跃
|
||||
elif can_double_jump:
|
||||
velocity.y = jump_force * 0.8 # 二段跳 (力度稍小)
|
||||
can_double_jump = false
|
||||
|
||||
# 重置二段跳能力
|
||||
if is_on_floor() and not can_double_jump:
|
||||
can_double_jump = true
|
||||
```
|
||||
|
||||
### 临时解决方案注释
|
||||
|
||||
```gdscript
|
||||
func handle_collision(body: Node2D):
|
||||
# TODO: 重构碰撞处理逻辑,当前实现过于复杂
|
||||
# FIXME: 在某些情况下会出现重复碰撞检测
|
||||
# HACK: 临时解决方案,等待 Godot 4.6 修复相关 bug
|
||||
|
||||
if body.has_method("take_damage"):
|
||||
# NOTE: 伤害计算需要考虑护甲和抗性
|
||||
var damage = calculate_damage(base_damage)
|
||||
body.take_damage(damage)
|
||||
```
|
||||
## 特殊注释标记
|
||||
|
||||
使用标准化的标记来标识不同类型的注释:
|
||||
|
||||
### 标记类型
|
||||
|
||||
```gdscript
|
||||
# TODO: 待实现的功能
|
||||
# TODO: 添加音效播放功能
|
||||
# TODO: 实现存档系统
|
||||
|
||||
# FIXME: 需要修复的问题
|
||||
# FIXME: 内存泄漏问题,需要及时释放资源
|
||||
# FIXME: 在低帧率下移动不流畅
|
||||
|
||||
# HACK: 临时解决方案
|
||||
# HACK: 绕过 Godot 引擎的已知 bug
|
||||
# HACK: 临时方案,等待更好的实现
|
||||
|
||||
# NOTE: 重要说明
|
||||
# NOTE: 此函数会修改全局状态,谨慎使用
|
||||
# NOTE: 性能敏感代码,避免频繁调用
|
||||
|
||||
# WARNING: 警告信息
|
||||
# WARNING: 不要在 _ready() 之前调用此函数
|
||||
# WARNING: 此操作不可逆,请确认后执行
|
||||
|
||||
# OPTIMIZE: 性能优化点
|
||||
# OPTIMIZE: 可以使用对象池优化内存分配
|
||||
# OPTIMIZE: 考虑使用缓存提高查询效率
|
||||
```
|
||||
|
||||
### 版本和兼容性标记
|
||||
|
||||
```gdscript
|
||||
# @since 1.0.0 - 添加基础移动功能
|
||||
# @deprecated 使用 new_movement_system() 替代
|
||||
# @godot_version 4.5+ - 需要 Godot 4.5 或更高版本
|
||||
# @platform_specific Windows/Linux - 仅在桌面平台可用
|
||||
```
|
||||
|
||||
## AI辅助注释指南
|
||||
|
||||
### 为AI提供的注释模板
|
||||
|
||||
当需要AI帮助补充注释时,请使用以下模板:
|
||||
|
||||
```gdscript
|
||||
# [AI_COMMENT_REQUEST]
|
||||
# 请为以下函数添加详细注释,包括:
|
||||
# - 功能描述
|
||||
# - 参数说明
|
||||
# - 返回值说明
|
||||
# - 使用示例
|
||||
# - 注意事项
|
||||
func complex_function(param1: String, param2: int) -> Dictionary:
|
||||
# 复杂的业务逻辑...
|
||||
pass
|
||||
```
|
||||
|
||||
### AI注释补充规则
|
||||
|
||||
1. **保持一致性** - 遵循项目现有的注释风格
|
||||
2. **关注业务逻辑** - 重点解释业务含义,而非语法
|
||||
3. **提供上下文** - 说明函数在整个系统中的作用
|
||||
4. **标注复杂度** - 对复杂算法提供额外说明
|
||||
|
||||
### 示例:AI补充前后对比
|
||||
|
||||
**补充前:**
|
||||
```gdscript
|
||||
func calculate_damage(base_damage: int, armor: int, resistance: float) -> int:
|
||||
var final_damage = base_damage
|
||||
final_damage -= armor
|
||||
final_damage *= (1.0 - resistance)
|
||||
return max(1, final_damage)
|
||||
```
|
||||
|
||||
**AI补充后:**
|
||||
```gdscript
|
||||
# 计算最终伤害值
|
||||
#
|
||||
# 根据基础伤害、护甲值和抗性计算实际造成的伤害
|
||||
# 伤害计算公式: (基础伤害 - 护甲) × (1 - 抗性)
|
||||
#
|
||||
# 参数:
|
||||
# base_damage: int - 基础伤害值
|
||||
# armor: int - 目标护甲值 (减少固定伤害)
|
||||
# resistance: float - 目标抗性 (0.0-1.0, 减少百分比伤害)
|
||||
#
|
||||
# 返回值:
|
||||
# int - 最终伤害值 (最小为1,确保至少造成1点伤害)
|
||||
#
|
||||
# 使用示例:
|
||||
# var damage = calculate_damage(100, 20, 0.3) # 结果: 56
|
||||
#
|
||||
# 注意事项:
|
||||
# - 护甲为负值时会增加伤害
|
||||
# - 抗性超过1.0时可能导致负伤害,但会被限制为1
|
||||
func calculate_damage(base_damage: int, armor: int, resistance: float) -> int:
|
||||
var final_damage = base_damage
|
||||
final_damage -= armor # 减去护甲值
|
||||
final_damage *= (1.0 - resistance) # 应用抗性
|
||||
return max(1, final_damage) # 确保最小伤害为1
|
||||
```
|
||||
## 注释最佳实践
|
||||
|
||||
### 什么时候需要注释
|
||||
|
||||
**必须添加注释的情况:**
|
||||
- 复杂的算法和业务逻辑
|
||||
- 非显而易见的设计决策
|
||||
- 临时解决方案和已知问题
|
||||
- 公共API和接口函数
|
||||
- 性能敏感的代码段
|
||||
- 平台特定或版本特定的代码
|
||||
|
||||
**不需要注释的情况:**
|
||||
- 显而易见的代码 (`var count = 0 # 计数器` ❌)
|
||||
- 重复函数名的注释 (`func get_name() -> String: # 获取名称` ❌)
|
||||
- 过时或错误的注释
|
||||
|
||||
### 注释维护原则
|
||||
|
||||
```gdscript
|
||||
# ✅ 好的注释 - 解释为什么这样做
|
||||
# 使用二分查找提高大数组的查询效率
|
||||
# 当数组长度超过100时,线性查找性能会显著下降
|
||||
func binary_search(array: Array, target: Variant) -> int:
|
||||
|
||||
# ❌ 坏的注释 - 重复代码内容
|
||||
# 遍历数组查找目标值
|
||||
func linear_search(array: Array, target: Variant) -> int:
|
||||
```
|
||||
|
||||
### 团队协作注释
|
||||
|
||||
```gdscript
|
||||
# 多人协作时的注释规范
|
||||
class_name NetworkManager
|
||||
|
||||
# @author: 张三 - 网络连接模块
|
||||
# @author: 李四 - 数据同步模块
|
||||
# @reviewer: 王五 - 代码审查
|
||||
# @last_modified: 2024-12-24
|
||||
|
||||
# 网络连接状态枚举
|
||||
#
|
||||
# 定义了客户端与服务器的连接状态
|
||||
# 状态转换: DISCONNECTED -> CONNECTING -> CONNECTED -> DISCONNECTED
|
||||
enum ConnectionState {
|
||||
DISCONNECTED, # 未连接
|
||||
CONNECTING, # 连接中
|
||||
CONNECTED, # 已连接
|
||||
RECONNECTING # 重连中 - @added by 李四 2024-12-20
|
||||
}
|
||||
```
|
||||
|
||||
## 注释检查清单
|
||||
|
||||
在提交代码前,请检查以下项目:
|
||||
|
||||
### 文件级别检查
|
||||
- [ ] 文件头注释完整(文件名、作用、主要功能)
|
||||
- [ ] 依赖关系说明清楚
|
||||
- [ ] 作者和创建时间已填写
|
||||
|
||||
### 函数级别检查
|
||||
- [ ] 公共函数有完整的参数和返回值说明
|
||||
- [ ] 复杂函数有使用示例
|
||||
- [ ] 特殊情况和注意事项已标注
|
||||
|
||||
### 代码级别检查
|
||||
- [ ] 复杂逻辑有行内注释说明
|
||||
- [ ] 魔法数字有常量定义和注释
|
||||
- [ ] TODO/FIXME 标记有明确的处理计划
|
||||
|
||||
### 质量检查
|
||||
- [ ] 注释内容准确,与代码一致
|
||||
- [ ] 中文表达清晰,无错别字
|
||||
- [ ] 注释格式符合项目规范
|
||||
|
||||
## 注释工具和插件
|
||||
|
||||
### Godot编辑器设置
|
||||
```
|
||||
# 在 Godot 编辑器中设置注释快捷键
|
||||
# 编辑器设置 -> 快捷键 -> 注释/取消注释: Ctrl+/
|
||||
```
|
||||
|
||||
### 推荐的注释插件
|
||||
- **GDScript Language Server** - 提供注释语法高亮
|
||||
- **Code Formatter** - 自动格式化注释
|
||||
- **Documentation Generator** - 自动生成API文档
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
良好的注释是代码质量的重要组成部分。遵循本规范可以:
|
||||
|
||||
1. **提高代码可读性** - 帮助团队成员快速理解代码
|
||||
2. **降低维护成本** - 减少后期修改和调试时间
|
||||
3. **促进知识传承** - 保留设计思路和业务逻辑
|
||||
4. **支持AI辅助开发** - 为AI提供更好的上下文信息
|
||||
|
||||
记住:**好的注释解释为什么,而不是做什么。**
|
||||
645
docs/02-开发规范/命名规范.md
Normal file
645
docs/02-开发规范/命名规范.md
Normal file
@@ -0,0 +1,645 @@
|
||||
# Godot 项目命名规范
|
||||
|
||||
本文档定义了 whaleTown 项目的命名规范,以保持代码的一致性和可读性。
|
||||
|
||||
## 目录
|
||||
|
||||
- [场景文件](#场景文件)
|
||||
- [脚本文件](#脚本文件)
|
||||
- [节点命名](#节点命名)
|
||||
- [变量命名](#变量命名)
|
||||
- [函数命名](#函数命名)
|
||||
- [常量命名](#常量命名)
|
||||
- [资源文件](#资源文件)
|
||||
- [目录结构](#目录结构)
|
||||
|
||||
---
|
||||
|
||||
## 场景文件
|
||||
|
||||
**规则**:下划线分隔 + `_scene` 或 `_prefab` 后缀
|
||||
|
||||
### 场景类型
|
||||
|
||||
- **主场景**:`_scene` 后缀,表示完整的游戏场景
|
||||
- **预制体**:`_prefab` 后缀,表示可复用的场景组件
|
||||
|
||||
### 示例
|
||||
|
||||
```
|
||||
✅ 正确
|
||||
main_scene.tscn # 主场景
|
||||
battle_scene.tscn # 战斗场景
|
||||
menu_scene.tscn # 菜单场景
|
||||
player_prefab.tscn # 玩家预制体
|
||||
enemy_boss_prefab.tscn # Boss 敌人预制体
|
||||
ui_dialog_prefab.tscn # 对话框预制体
|
||||
|
||||
❌ 错误
|
||||
MainScene.tscn # 不使用大驼峰
|
||||
main.tscn # 缺少 _scene 后缀
|
||||
player.tscn # 预制体缺少 _prefab 后缀
|
||||
```
|
||||
|
||||
### 命名建议
|
||||
|
||||
- 场景名称应清晰表达场景用途
|
||||
- 多个单词使用下划线分隔
|
||||
- 避免使用缩写,除非是通用缩写(如 ui、hp)
|
||||
|
||||
---
|
||||
|
||||
## 脚本文件
|
||||
|
||||
**规则**:大驼峰命名(PascalCase),`.gd` 扩展名
|
||||
|
||||
### 脚本类型
|
||||
|
||||
- **控制器脚本**:`Controller` 后缀
|
||||
- **管理器脚本**:`Manager` 后缀
|
||||
- **UI 脚本**:`UI_` 前缀
|
||||
- **数据类**:`Data` 后缀
|
||||
- **工具类**:`Utils` 或 `Helper` 后缀
|
||||
|
||||
### 示例
|
||||
|
||||
```
|
||||
✅ 正确
|
||||
PlayerController.gd # 玩家控制器
|
||||
EnemyAI.gd # 敌人 AI
|
||||
GameManager.gd # 游戏管理器
|
||||
AudioManager.gd # 音频管理器
|
||||
UI_MainMenu.gd # 主菜单 UI
|
||||
UI_HealthBar.gd # 血条 UI
|
||||
PlayerData.gd # 玩家数据类
|
||||
SaveData.gd # 存档数据类
|
||||
MathUtils.gd # 数学工具类
|
||||
StringHelper.gd # 字符串辅助类
|
||||
|
||||
❌ 错误
|
||||
player_controller.gd # 不使用下划线
|
||||
playerController.gd # 不使用小驼峰
|
||||
player.gd # 缺少明确的类型后缀
|
||||
PLAYER.gd # 不使用全大写
|
||||
```
|
||||
|
||||
### 命名建议
|
||||
|
||||
- 脚本名称应与类名一致
|
||||
- 一个脚本文件只包含一个主要类
|
||||
- 使用清晰的后缀表明脚本用途
|
||||
|
||||
---
|
||||
|
||||
## 节点命名
|
||||
|
||||
**规则**:小驼峰命名(camelCase)
|
||||
|
||||
### 节点类型
|
||||
|
||||
- **UI 节点**:描述性名称,如 `startButton`、`healthBar`
|
||||
- **游戏对象**:对象名称,如 `player`、`enemy`
|
||||
- **容器节点**:用途 + Container,如 `itemContainer`
|
||||
- **特效节点**:效果 + Effect,如 `explosionEffect`
|
||||
|
||||
### 示例
|
||||
|
||||
```
|
||||
✅ 正确
|
||||
player # 玩家节点
|
||||
enemy # 敌人节点
|
||||
mainCamera # 主相机
|
||||
playerHpBar # 玩家血条
|
||||
startButton # 开始按钮
|
||||
pauseMenu # 暂停菜单
|
||||
itemContainer # 物品容器
|
||||
backgroundMusic # 背景音乐
|
||||
explosionEffect # 爆炸特效
|
||||
collisionShape # 碰撞形状
|
||||
|
||||
❌ 错误
|
||||
Player # 不使用大驼峰
|
||||
player_hp_bar # 不使用下划线
|
||||
StartButton # 不使用大驼峰
|
||||
PLAYER # 不使用全大写
|
||||
btn_start # 避免缩写
|
||||
```
|
||||
|
||||
### 命名建议
|
||||
|
||||
- 节点名称应简洁明了
|
||||
- 同类型节点可以添加数字后缀,如 `enemy1`、`enemy2`
|
||||
- 避免使用无意义的名称,如 `Node2D`、`Control`
|
||||
|
||||
---
|
||||
|
||||
## 变量命名
|
||||
|
||||
**规则**:小驼峰命名(camelCase),可选类型前缀
|
||||
|
||||
### 变量类型前缀(可选)
|
||||
|
||||
使用类型前缀可以提升代码可读性,但不是强制要求。
|
||||
|
||||
| 类型 | 前缀 | 示例 |
|
||||
|------|------|------|
|
||||
| 整数 | `i` | `iHealth`, `iScore` |
|
||||
| 浮点数 | `f` | `fSpeed`, `fDamage` |
|
||||
| 字符串 | `s` | `sPlayerName`, `sDialogText` |
|
||||
| 布尔值 | `b` 或 `is/has` | `bIsAlive`, `isJumping`, `hasKey` |
|
||||
| 数组 | `a` 或复数 | `aEnemies`, `enemies` |
|
||||
| 字典 | `d` | `dPlayerStats` |
|
||||
| 节点引用 | 无前缀 | `playerNode`, `healthBar` |
|
||||
|
||||
### 示例
|
||||
|
||||
```gdscript
|
||||
✅ 正确(带前缀)
|
||||
var iHealth: int = 100
|
||||
var fSpeed: float = 5.0
|
||||
var sPlayerName: String = "Player"
|
||||
var bIsAlive: bool = true
|
||||
var aEnemies: Array = []
|
||||
var dInventory: Dictionary = {}
|
||||
|
||||
✅ 正确(不带前缀)
|
||||
var health: int = 100
|
||||
var speed: float = 5.0
|
||||
var playerName: String = "Player"
|
||||
var isAlive: bool = true
|
||||
var enemies: Array = []
|
||||
var inventory: Dictionary = {}
|
||||
|
||||
✅ 正确(布尔值使用 is/has)
|
||||
var isJumping: bool = false
|
||||
var hasWeapon: bool = true
|
||||
var canMove: bool = true
|
||||
|
||||
❌ 错误
|
||||
var Health: int = 100 # 不使用大驼峰
|
||||
var player_name: String = "" # 不使用下划线
|
||||
var SPEED: float = 5.0 # 常量才使用全大写
|
||||
var i: int = 0 # 循环变量除外,其他避免单字母
|
||||
```
|
||||
|
||||
### 成员变量
|
||||
|
||||
```gdscript
|
||||
# 导出变量(Inspector 可见)
|
||||
@export var maxHealth: int = 100
|
||||
@export var moveSpeed: float = 200.0
|
||||
@export var playerName: String = "Player"
|
||||
|
||||
# 私有变量(以下划线开头)
|
||||
var _currentHealth: int
|
||||
var _isInitialized: bool = false
|
||||
|
||||
# 节点引用
|
||||
@onready var healthBar: ProgressBar = $HealthBar
|
||||
@onready var animationPlayer: AnimationPlayer = $AnimationPlayer
|
||||
```
|
||||
|
||||
### 命名建议
|
||||
|
||||
- 变量名应具有描述性
|
||||
- 布尔变量使用 `is`、`has`、`can` 等前缀
|
||||
- 私有变量以下划线 `_` 开头
|
||||
- 避免使用单字母变量名(循环变量 `i`、`j` 除外)
|
||||
|
||||
---
|
||||
|
||||
## 函数命名
|
||||
|
||||
**规则**:小驼峰命名(camelCase),动词开头
|
||||
|
||||
### 函数类型
|
||||
|
||||
- **获取函数**:`get` 前缀
|
||||
- **设置函数**:`set` 前缀
|
||||
- **判断函数**:`is`、`has`、`can` 前缀,返回布尔值
|
||||
- **事件处理**:`on` 或 `_on` 前缀
|
||||
- **初始化函数**:`init` 或 `initialize`
|
||||
- **更新函数**:`update` 前缀
|
||||
- **私有函数**:`_` 前缀
|
||||
|
||||
### 示例
|
||||
|
||||
```gdscript
|
||||
✅ 正确
|
||||
func getPlayerPosition() -> Vector2:
|
||||
return position
|
||||
|
||||
func setHealth(value: int) -> void:
|
||||
health = value
|
||||
|
||||
func isAlive() -> bool:
|
||||
return health > 0
|
||||
|
||||
func hasItem(itemName: String) -> bool:
|
||||
return inventory.has(itemName)
|
||||
|
||||
func canJump() -> bool:
|
||||
return isOnFloor and not isJumping
|
||||
|
||||
func onButtonPressed() -> void:
|
||||
print("Button pressed")
|
||||
|
||||
func _onTimerTimeout() -> void:
|
||||
# 信号处理函数
|
||||
pass
|
||||
|
||||
func initializePlayer() -> void:
|
||||
health = maxHealth
|
||||
position = startPosition
|
||||
|
||||
func updateMovement(delta: float) -> void:
|
||||
position += velocity * delta
|
||||
|
||||
func _calculateDamage(baseDamage: int) -> int:
|
||||
# 私有函数
|
||||
return baseDamage * damageMultiplier
|
||||
|
||||
❌ 错误
|
||||
func GetPlayerPosition(): # 不使用大驼峰
|
||||
func get_player_position(): # 不使用下划线
|
||||
func PlayerPosition(): # 缺少动词
|
||||
func JUMP(): # 不使用全大写
|
||||
```
|
||||
|
||||
### 内置函数重写
|
||||
|
||||
```gdscript
|
||||
# Godot 内置函数使用下划线命名(保持原样)
|
||||
func _ready() -> void:
|
||||
pass
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
pass
|
||||
```
|
||||
|
||||
### 命名建议
|
||||
|
||||
- 函数名应清晰表达功能
|
||||
- 使用动词开头,如 `get`、`set`、`update`、`calculate`
|
||||
- 参数名称也使用小驼峰命名
|
||||
- 私有函数以下划线开头
|
||||
|
||||
---
|
||||
|
||||
## 常量命名
|
||||
|
||||
**规则**:全大写 + 下划线分隔
|
||||
|
||||
### 示例
|
||||
|
||||
```gdscript
|
||||
✅ 正确
|
||||
const MAX_HEALTH: int = 100
|
||||
const DEFAULT_SPEED: float = 200.0
|
||||
const PLAYER_NAME: String = "Player"
|
||||
const GRAVITY: float = 980.0
|
||||
const JUMP_FORCE: float = -400.0
|
||||
const MAX_ENEMIES: int = 10
|
||||
const SAVE_FILE_PATH: String = "user://save.dat"
|
||||
const SCREEN_WIDTH: int = 1920
|
||||
const SCREEN_HEIGHT: int = 1080
|
||||
|
||||
❌ 错误
|
||||
const maxHealth: int = 100 # 不使用小驼峰
|
||||
const MaxHealth: int = 100 # 不使用大驼峰
|
||||
const max_health: int = 100 # 不使用小写
|
||||
```
|
||||
|
||||
### 枚举命名
|
||||
|
||||
```gdscript
|
||||
# 枚举名使用大驼峰
|
||||
enum PlayerState {
|
||||
IDLE, # 枚举值使用全大写
|
||||
WALKING,
|
||||
RUNNING,
|
||||
JUMPING,
|
||||
ATTACKING
|
||||
}
|
||||
|
||||
enum EnemyType {
|
||||
NORMAL,
|
||||
ELITE,
|
||||
BOSS
|
||||
}
|
||||
|
||||
# 使用
|
||||
var currentState: PlayerState = PlayerState.IDLE
|
||||
```
|
||||
|
||||
### 命名建议
|
||||
|
||||
- 常量名应清晰表达含义
|
||||
- 使用全大写和下划线
|
||||
- 枚举类型名使用大驼峰,枚举值使用全大写
|
||||
|
||||
---
|
||||
|
||||
## 资源文件
|
||||
|
||||
**规则**:下划线分隔,小写字母
|
||||
|
||||
### 图片资源
|
||||
|
||||
```
|
||||
✅ 正确
|
||||
bg_main_menu.png # 背景图
|
||||
sprite_player_idle.png # 精灵图
|
||||
icon_sword.png # 图标
|
||||
ui_button_normal.png # UI 元素
|
||||
tile_grass_01.png # 瓦片图
|
||||
|
||||
❌ 错误
|
||||
BgMainMenu.png # 不使用大驼峰
|
||||
bg-main-menu.png # 不使用连字符
|
||||
BACKGROUND.png # 不使用全大写
|
||||
```
|
||||
|
||||
### 音频资源
|
||||
|
||||
```
|
||||
✅ 正确
|
||||
sound_jump.wav # 音效
|
||||
sound_explosion.wav
|
||||
music_battle.ogg # 音乐
|
||||
music_menu.ogg
|
||||
voice_npc_greeting.wav # 语音
|
||||
|
||||
❌ 错误
|
||||
Jump.wav
|
||||
sound-jump.wav
|
||||
SOUND_JUMP.wav
|
||||
```
|
||||
|
||||
### 其他资源
|
||||
|
||||
```
|
||||
✅ 正确
|
||||
font_main.ttf # 字体
|
||||
shader_water.gdshader # 着色器
|
||||
material_metal.tres # 材质
|
||||
animation_walk.res # 动画
|
||||
|
||||
❌ 错误
|
||||
MainFont.ttf
|
||||
font-main.ttf
|
||||
FONT_MAIN.ttf
|
||||
```
|
||||
|
||||
### 资源命名建议
|
||||
|
||||
- 使用类型前缀,如 `bg_`、`sprite_`、`sound_`、`music_`
|
||||
- 多个单词使用下划线分隔
|
||||
- 全部使用小写字母
|
||||
- 同系列资源使用数字后缀,如 `tile_01.png`、`tile_02.png`
|
||||
|
||||
### 扩展资源类型
|
||||
|
||||
```
|
||||
✅ 正确
|
||||
# 材质资源
|
||||
material_metal.tres # 金属材质
|
||||
material_wood.tres # 木材材质
|
||||
material_water.tres # 水材质
|
||||
|
||||
# 着色器资源
|
||||
shader_water.gdshader # 水着色器
|
||||
shader_fire.gdshader # 火焰着色器
|
||||
shader_outline.gdshader # 轮廓着色器
|
||||
|
||||
# 特效资源
|
||||
fx_explosion.png # 爆炸特效
|
||||
fx_magic_circle.png # 魔法阵特效
|
||||
fx_damage_numbers.png # 伤害数字特效
|
||||
|
||||
# 环境资源
|
||||
obj_tree.png # 树木对象
|
||||
obj_rock.png # 岩石对象
|
||||
tile_grass_01.png # 草地瓦片
|
||||
tile_stone_01.png # 石头瓦片
|
||||
|
||||
❌ 错误
|
||||
MetalMaterial.tres # 不使用大驼峰
|
||||
material-wood.tres # 不使用连字符
|
||||
SHADER_WATER.gdshader # 不使用全大写
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 目录结构
|
||||
|
||||
### 目录命名
|
||||
|
||||
**规则**:小写字母 + 下划线分隔
|
||||
|
||||
```
|
||||
✅ 正确
|
||||
scenes/ # 场景目录
|
||||
scripts/ # 脚本目录
|
||||
assets/ # 资源目录
|
||||
sprites/ # 精灵图
|
||||
sounds/ # 音效
|
||||
music/ # 音乐
|
||||
fonts/ # 字体
|
||||
materials/ # 材质
|
||||
shaders/ # 着色器
|
||||
data/ # 数据目录
|
||||
levels/ # 关卡数据
|
||||
configs/ # 配置文件
|
||||
dialogues/ # 对话数据
|
||||
localization/ # 本地化数据
|
||||
core/ # 核心系统目录
|
||||
managers/ # 管理器
|
||||
systems/ # 系统组件
|
||||
utils/ # 工具类
|
||||
components/ # 通用组件
|
||||
interfaces/ # 接口定义
|
||||
module/ # 模块目录
|
||||
UI/ # UI模块
|
||||
Character/ # 角色模块
|
||||
Inventory/ # 背包模块
|
||||
Combat/ # 战斗模块
|
||||
Dialogue/ # 对话模块
|
||||
addons/ # 插件目录
|
||||
tests/ # 测试目录
|
||||
unit/ # 单元测试
|
||||
integration/ # 集成测试
|
||||
ui/ # UI测试
|
||||
performance/ # 性能测试
|
||||
docs/ # 文档目录
|
||||
auth/ # 认证相关文档
|
||||
|
||||
❌ 错误
|
||||
Scenes/ # 不使用大写
|
||||
Scripts/
|
||||
Assets/
|
||||
```
|
||||
|
||||
### 推荐的项目结构
|
||||
|
||||
```
|
||||
whaleTown/
|
||||
├── scenes/
|
||||
│ ├── main_scene.tscn
|
||||
│ ├── battle_scene.tscn
|
||||
│ └── prefabs/
|
||||
│ ├── player_prefab.tscn
|
||||
│ └── enemy_prefab.tscn
|
||||
├── scripts/
|
||||
│ ├── PlayerController.gd
|
||||
│ ├── EnemyAI.gd
|
||||
│ ├── GameManager.gd
|
||||
│ └── ui/
|
||||
│ ├── UI_MainMenu.gd
|
||||
│ └── UI_HealthBar.gd
|
||||
├── assets/
|
||||
│ ├── sprites/
|
||||
│ │ ├── sprite_player_idle.png
|
||||
│ │ └── sprite_enemy_walk.png
|
||||
│ ├── sounds/
|
||||
│ │ ├── sound_jump.wav
|
||||
│ │ └── sound_attack.wav
|
||||
│ ├── music/
|
||||
│ │ └── music_battle.ogg
|
||||
│ └── fonts/
|
||||
│ └── font_main.ttf
|
||||
├── data/
|
||||
│ ├── levels/
|
||||
│ │ └── level_01.json
|
||||
│ └── configs/
|
||||
│ └── game_config.json
|
||||
└── addons/
|
||||
└── plugin_name/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整示例
|
||||
|
||||
### 场景文件:`player_prefab.tscn`
|
||||
|
||||
```
|
||||
[节点树]
|
||||
player # 根节点(小驼峰)
|
||||
├── sprite # 精灵节点
|
||||
├── collisionShape # 碰撞形状
|
||||
├── animationPlayer # 动画播放器
|
||||
└── healthBar # 血条
|
||||
```
|
||||
|
||||
### 脚本文件:`PlayerController.gd`
|
||||
|
||||
```gdscript
|
||||
extends CharacterBody2D
|
||||
|
||||
# 常量(全大写 + 下划线)
|
||||
const MAX_HEALTH: int = 100
|
||||
const DEFAULT_SPEED: float = 200.0
|
||||
const JUMP_FORCE: float = -400.0
|
||||
|
||||
# 导出变量(小驼峰)
|
||||
@export var moveSpeed: float = DEFAULT_SPEED
|
||||
@export var maxHealth: int = MAX_HEALTH
|
||||
|
||||
# 成员变量(小驼峰,可选类型前缀)
|
||||
var currentHealth: int
|
||||
var isJumping: bool = false
|
||||
var hasDoubleJump: bool = false
|
||||
|
||||
# 私有变量(下划线前缀)
|
||||
var _velocity: Vector2 = Vector2.ZERO
|
||||
var _isInitialized: bool = false
|
||||
|
||||
# 节点引用
|
||||
@onready var sprite: Sprite2D = $sprite
|
||||
@onready var animationPlayer: AnimationPlayer = $animationPlayer
|
||||
@onready var healthBar: ProgressBar = $healthBar
|
||||
|
||||
# 内置函数
|
||||
func _ready() -> void:
|
||||
initializePlayer()
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
updateMovement(delta)
|
||||
updateAnimation()
|
||||
|
||||
# 公共函数(小驼峰,动词开头)
|
||||
func initializePlayer() -> void:
|
||||
currentHealth = maxHealth
|
||||
updateHealthBar()
|
||||
_isInitialized = true
|
||||
|
||||
func takeDamage(damage: int) -> void:
|
||||
currentHealth -= damage
|
||||
if currentHealth <= 0:
|
||||
die()
|
||||
updateHealthBar()
|
||||
|
||||
func heal(amount: int) -> void:
|
||||
currentHealth = min(currentHealth + amount, maxHealth)
|
||||
updateHealthBar()
|
||||
|
||||
func isAlive() -> bool:
|
||||
return currentHealth > 0
|
||||
|
||||
func canJump() -> bool:
|
||||
return is_on_floor() or hasDoubleJump
|
||||
|
||||
# 私有函数(下划线前缀)
|
||||
func _calculateDamage(baseDamage: int) -> int:
|
||||
return baseDamage * 2
|
||||
|
||||
func updateMovement(delta: float) -> void:
|
||||
# 移动逻辑
|
||||
pass
|
||||
|
||||
func updateAnimation() -> void:
|
||||
# 动画逻辑
|
||||
pass
|
||||
|
||||
func updateHealthBar() -> void:
|
||||
if healthBar:
|
||||
healthBar.value = float(currentHealth) / float(maxHealth) * 100.0
|
||||
|
||||
func die() -> void:
|
||||
print("Player died")
|
||||
queue_free()
|
||||
|
||||
# 信号处理(_on 前缀)
|
||||
func _onAreaEntered(area: Area2D) -> void:
|
||||
print("Area entered: ", area.name)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
遵循统一的命名规范可以:
|
||||
|
||||
- ✅ 提高代码可读性
|
||||
- ✅ 便于团队协作
|
||||
- ✅ 减少命名冲突
|
||||
- ✅ 方便代码维护
|
||||
- ✅ 提升开发效率
|
||||
|
||||
记住核心原则:
|
||||
|
||||
1. **场景文件**:`下划线_scene/prefab`
|
||||
2. **脚本文件**:`PascalCase.gd`
|
||||
3. **节点名称**:`camelCase`
|
||||
4. **变量/函数**:`camelCase`
|
||||
5. **常量**:`UPPER_CASE`
|
||||
6. **资源文件**:`lower_case`
|
||||
|
||||
保持一致,代码更清晰!
|
||||
406
docs/02-开发规范/开发哲学与最佳实践.md
Normal file
406
docs/02-开发规范/开发哲学与最佳实践.md
Normal file
@@ -0,0 +1,406 @@
|
||||
# 开发哲学与最佳实践
|
||||
|
||||
本文档阐述了WhaleTown项目的开发哲学和编程最佳实践,旨在指导团队创造高质量、可维护的代码。
|
||||
|
||||
## 🧘 开发哲学
|
||||
|
||||
### 核心理念
|
||||
- **用户体验至上** - 每个功能都要考虑用户感受
|
||||
- **代码即文档** - 代码应该自解释,清晰易懂
|
||||
- **简洁胜于复杂** - 优先选择简单直接的解决方案
|
||||
- **质量重于速度** - 宁可慢一点,也要做对
|
||||
|
||||
### 设计原则
|
||||
|
||||
#### 1. 流畅体验 (Juice or Death)
|
||||
每个用户交互都必须有视觉反馈和动画效果:
|
||||
|
||||
```gdscript
|
||||
# ✅ 正确:为UI交互添加动画
|
||||
func show_dialog() -> void:
|
||||
dialog.modulate.a = 0.0
|
||||
dialog.scale = Vector2(0.8, 0.8)
|
||||
dialog.visible = true
|
||||
|
||||
var tween = create_tween()
|
||||
tween.parallel().tween_property(dialog, "modulate:a", 1.0, 0.3)
|
||||
tween.parallel().tween_property(dialog, "scale", Vector2.ONE, 0.3)
|
||||
tween.set_ease(Tween.EASE_OUT)
|
||||
tween.set_trans(Tween.TRANS_BACK)
|
||||
|
||||
# ❌ 错误:没有动画的生硬切换
|
||||
func show_dialog() -> void:
|
||||
dialog.visible = true # 突然出现,体验差
|
||||
```
|
||||
|
||||
#### 2. 零魔法数字 (Zero Magic Numbers)
|
||||
所有数值都应该有明确的含义和来源:
|
||||
|
||||
```gdscript
|
||||
# ✅ 正确:使用导出变量或配置文件
|
||||
@export var move_speed: float = 200.0
|
||||
@export var jump_height: float = 400.0
|
||||
@export var health_max: int = 100
|
||||
|
||||
# 或从配置文件加载
|
||||
const CONFIG_PATH = "res://data/player_config.json"
|
||||
var config_data: Dictionary
|
||||
|
||||
func _ready() -> void:
|
||||
config_data = load_config(CONFIG_PATH)
|
||||
move_speed = config_data.get("move_speed", 200.0)
|
||||
|
||||
# ❌ 错误:硬编码的魔法数字
|
||||
func _physics_process(delta: float) -> void:
|
||||
velocity.x = input_direction.x * 200 # 200是什么?
|
||||
if position.y > 1000: # 1000代表什么?
|
||||
respawn()
|
||||
```
|
||||
|
||||
#### 3. 函数单一职责
|
||||
每个函数只做一件事,做好一件事:
|
||||
|
||||
```gdscript
|
||||
# ✅ 正确:职责分离
|
||||
func handle_player_input() -> void:
|
||||
var input_direction = get_input_direction()
|
||||
apply_movement(input_direction)
|
||||
check_interaction_input()
|
||||
|
||||
func get_input_direction() -> Vector2:
|
||||
return Input.get_vector("move_left", "move_right", "move_up", "move_down")
|
||||
|
||||
func apply_movement(direction: Vector2) -> void:
|
||||
velocity = direction * move_speed
|
||||
move_and_slide()
|
||||
|
||||
func check_interaction_input() -> void:
|
||||
if Input.is_action_just_pressed("interact"):
|
||||
try_interact()
|
||||
|
||||
# ❌ 错误:一个函数做太多事
|
||||
func handle_everything() -> void:
|
||||
# 处理输入
|
||||
var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
|
||||
# 处理移动
|
||||
velocity = direction * move_speed
|
||||
move_and_slide()
|
||||
# 处理交互
|
||||
if Input.is_action_just_pressed("interact"):
|
||||
# 检查交互对象
|
||||
var interactables = get_nearby_interactables()
|
||||
# 执行交互
|
||||
for obj in interactables:
|
||||
obj.interact()
|
||||
# 更新UI
|
||||
update_health_bar()
|
||||
# 播放音效
|
||||
play_footstep_sound()
|
||||
```
|
||||
|
||||
#### 4. 隐藏复杂性
|
||||
复杂的逻辑应该被封装,对外提供简洁的接口:
|
||||
|
||||
```gdscript
|
||||
# ✅ 正确:封装复杂逻辑
|
||||
class_name NetworkManager
|
||||
|
||||
func login(username: String, password: String, callback: Callable) -> int:
|
||||
return _make_request("POST", "/auth/login", {
|
||||
"username": username,
|
||||
"password": password
|
||||
}, callback)
|
||||
|
||||
func _make_request(method: String, endpoint: String, data: Dictionary, callback: Callable) -> int:
|
||||
# 复杂的网络请求逻辑被隐藏
|
||||
var request = HTTPRequest.new()
|
||||
var request_id = _generate_request_id()
|
||||
|
||||
# 设置请求头、处理认证、错误重试等复杂逻辑
|
||||
_setup_request_headers(request)
|
||||
_handle_authentication(request)
|
||||
_setup_retry_logic(request, callback)
|
||||
|
||||
return request_id
|
||||
|
||||
# 使用时非常简洁
|
||||
func _on_login_button_pressed() -> void:
|
||||
NetworkManager.login(username_input.text, password_input.text, _on_login_response)
|
||||
```
|
||||
|
||||
## 📋 编码最佳实践
|
||||
|
||||
### 1. 类型安全
|
||||
始终使用严格的类型声明:
|
||||
|
||||
```gdscript
|
||||
# ✅ 正确:明确的类型声明
|
||||
var player_health: int = 100
|
||||
var move_speed: float = 200.0
|
||||
var player_name: String = "Player"
|
||||
var inventory_items: Array[Item] = []
|
||||
var config_data: Dictionary = {}
|
||||
|
||||
func calculate_damage(base_damage: int, multiplier: float) -> int:
|
||||
return int(base_damage * multiplier)
|
||||
|
||||
# ❌ 错误:缺少类型信息
|
||||
var health = 100 # 类型不明确
|
||||
var speed = 200 # 可能是int也可能是float
|
||||
|
||||
func calculate_damage(base, mult): # 参数类型不明确
|
||||
return base * mult # 返回类型不明确
|
||||
```
|
||||
|
||||
### 2. 错误处理
|
||||
主动处理可能的错误情况:
|
||||
|
||||
```gdscript
|
||||
# ✅ 正确:完善的错误处理
|
||||
func load_save_file(file_path: String) -> Dictionary:
|
||||
if not FileAccess.file_exists(file_path):
|
||||
push_warning("存档文件不存在: " + file_path)
|
||||
return {}
|
||||
|
||||
var file = FileAccess.open(file_path, FileAccess.READ)
|
||||
if file == null:
|
||||
push_error("无法打开存档文件: " + file_path)
|
||||
return {}
|
||||
|
||||
var json_string = file.get_as_text()
|
||||
file.close()
|
||||
|
||||
if json_string.is_empty():
|
||||
push_warning("存档文件为空: " + file_path)
|
||||
return {}
|
||||
|
||||
var json = JSON.new()
|
||||
var parse_result = json.parse(json_string)
|
||||
if parse_result != OK:
|
||||
push_error("存档文件JSON格式错误: " + file_path)
|
||||
return {}
|
||||
|
||||
return json.data
|
||||
|
||||
# ❌ 错误:没有错误处理
|
||||
func load_save_file(file_path: String) -> Dictionary:
|
||||
var file = FileAccess.open(file_path, FileAccess.READ)
|
||||
var json_string = file.get_as_text()
|
||||
file.close()
|
||||
var json = JSON.new()
|
||||
json.parse(json_string)
|
||||
return json.data # 任何步骤出错都会崩溃
|
||||
```
|
||||
|
||||
### 3. 资源管理
|
||||
及时释放不需要的资源:
|
||||
|
||||
```gdscript
|
||||
# ✅ 正确:资源管理
|
||||
class_name AudioManager
|
||||
|
||||
var audio_players: Array[AudioStreamPlayer] = []
|
||||
var max_concurrent_sounds: int = 10
|
||||
|
||||
func play_sound(sound: AudioStream, volume: float = 0.0) -> void:
|
||||
# 清理已完成的音频播放器
|
||||
_cleanup_finished_players()
|
||||
|
||||
# 限制并发音频数量
|
||||
if audio_players.size() >= max_concurrent_sounds:
|
||||
_stop_oldest_player()
|
||||
|
||||
var player = AudioStreamPlayer.new()
|
||||
add_child(player)
|
||||
player.stream = sound
|
||||
player.volume_db = volume
|
||||
player.finished.connect(_on_audio_finished.bind(player))
|
||||
player.play()
|
||||
|
||||
audio_players.append(player)
|
||||
|
||||
func _cleanup_finished_players() -> void:
|
||||
audio_players = audio_players.filter(func(player): return player.playing)
|
||||
|
||||
func _on_audio_finished(player: AudioStreamPlayer) -> void:
|
||||
audio_players.erase(player)
|
||||
player.queue_free()
|
||||
```
|
||||
|
||||
### 4. 性能优化
|
||||
编写高效的代码:
|
||||
|
||||
```gdscript
|
||||
# ✅ 正确:性能优化的代码
|
||||
class_name EnemyManager
|
||||
|
||||
var enemies: Array[Enemy] = []
|
||||
var update_timer: float = 0.0
|
||||
const UPDATE_INTERVAL: float = 0.1 # 每100ms更新一次
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
update_timer += delta
|
||||
if update_timer >= UPDATE_INTERVAL:
|
||||
_update_enemies(update_timer)
|
||||
update_timer = 0.0
|
||||
|
||||
func _update_enemies(delta_time: float) -> void:
|
||||
# 只更新屏幕附近的敌人
|
||||
var camera_pos = get_viewport().get_camera_2d().global_position
|
||||
var screen_size = get_viewport().get_visible_rect().size
|
||||
|
||||
for enemy in enemies:
|
||||
if _is_enemy_near_screen(enemy, camera_pos, screen_size):
|
||||
enemy.update_ai(delta_time)
|
||||
|
||||
func _is_enemy_near_screen(enemy: Enemy, camera_pos: Vector2, screen_size: Vector2) -> bool:
|
||||
var distance = enemy.global_position.distance_to(camera_pos)
|
||||
var max_distance = screen_size.length() * 0.6 # 屏幕对角线的60%
|
||||
return distance <= max_distance
|
||||
|
||||
# ❌ 错误:性能问题
|
||||
func _process(delta: float) -> void:
|
||||
# 每帧更新所有敌人,无论是否可见
|
||||
for enemy in enemies:
|
||||
enemy.update_ai(delta) # 可能有数百个敌人
|
||||
# 每帧进行复杂计算
|
||||
var path = enemy.find_path_to_player()
|
||||
enemy.follow_path(path)
|
||||
```
|
||||
|
||||
## 🎯 代码审查标准
|
||||
|
||||
### 审查清单
|
||||
在提交代码前,请检查以下项目:
|
||||
|
||||
#### 功能性
|
||||
- [ ] 代码实现了预期功能
|
||||
- [ ] 处理了边界情况和错误情况
|
||||
- [ ] 添加了必要的测试用例
|
||||
|
||||
#### 可读性
|
||||
- [ ] 变量和函数名称清晰明确
|
||||
- [ ] 代码结构逻辑清晰
|
||||
- [ ] 添加了必要的注释
|
||||
|
||||
#### 性能
|
||||
- [ ] 避免了不必要的计算
|
||||
- [ ] 正确管理了资源生命周期
|
||||
- [ ] 使用了合适的数据结构
|
||||
|
||||
#### 规范性
|
||||
- [ ] 遵循了项目命名规范
|
||||
- [ ] 使用了正确的类型声明
|
||||
- [ ] 符合架构设计原则
|
||||
|
||||
### 代码示例评分
|
||||
|
||||
#### 优秀代码示例 (A级)
|
||||
```gdscript
|
||||
extends CharacterBody2D
|
||||
class_name Player
|
||||
|
||||
## 玩家角色控制器
|
||||
##
|
||||
## 负责处理玩家输入、移动和基础交互
|
||||
## 使用事件系统与其他组件通信
|
||||
|
||||
@export_group("Movement")
|
||||
@export var move_speed: float = 200.0
|
||||
@export var acceleration: float = 1000.0
|
||||
@export var friction: float = 800.0
|
||||
|
||||
@export_group("Interaction")
|
||||
@export var interaction_range: float = 50.0
|
||||
|
||||
@onready var sprite: Sprite2D = %Sprite2D
|
||||
@onready var animation_player: AnimationPlayer = %AnimationPlayer
|
||||
@onready var interaction_area: Area2D = %InteractionArea
|
||||
|
||||
var _current_interactable: Interactable = null
|
||||
|
||||
func _ready() -> void:
|
||||
_setup_interaction_area()
|
||||
_connect_signals()
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
_handle_movement(delta)
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed("interact"):
|
||||
_try_interact()
|
||||
|
||||
func _handle_movement(delta: float) -> void:
|
||||
var input_direction := _get_movement_input()
|
||||
_apply_movement(input_direction, delta)
|
||||
_update_animation(input_direction)
|
||||
|
||||
func _get_movement_input() -> Vector2:
|
||||
return Input.get_vector("move_left", "move_right", "move_up", "move_down")
|
||||
|
||||
func _apply_movement(direction: Vector2, delta: float) -> void:
|
||||
if direction != Vector2.ZERO:
|
||||
velocity = velocity.move_toward(direction * move_speed, acceleration * delta)
|
||||
else:
|
||||
velocity = velocity.move_toward(Vector2.ZERO, friction * delta)
|
||||
|
||||
move_and_slide()
|
||||
|
||||
func _update_animation(direction: Vector2) -> void:
|
||||
if direction.length() > 0.1:
|
||||
animation_player.play("walk")
|
||||
sprite.flip_h = direction.x < 0
|
||||
else:
|
||||
animation_player.play("idle")
|
||||
```
|
||||
|
||||
#### 需要改进的代码 (C级)
|
||||
```gdscript
|
||||
extends CharacterBody2D
|
||||
|
||||
var speed = 200
|
||||
var player
|
||||
var enemies = []
|
||||
|
||||
func _ready():
|
||||
player = self
|
||||
|
||||
func _process(delta):
|
||||
var dir = Vector2()
|
||||
if Input.is_action_pressed("ui_left"):
|
||||
dir.x -= 1
|
||||
if Input.is_action_pressed("ui_right"):
|
||||
dir.x += 1
|
||||
if Input.is_action_pressed("ui_up"):
|
||||
dir.y -= 1
|
||||
if Input.is_action_pressed("ui_down"):
|
||||
dir.y += 1
|
||||
|
||||
velocity = dir * speed
|
||||
move_and_slide()
|
||||
|
||||
for enemy in enemies:
|
||||
if position.distance_to(enemy.position) < 100:
|
||||
print("near enemy")
|
||||
```
|
||||
|
||||
## 🚀 持续改进
|
||||
|
||||
### 重构指导原则
|
||||
1. **小步快跑** - 每次只重构一小部分
|
||||
2. **测试保护** - 重构前确保有测试覆盖
|
||||
3. **功能不变** - 重构不改变外部行为
|
||||
4. **逐步优化** - 持续改进代码质量
|
||||
|
||||
### 技术债务管理
|
||||
```gdscript
|
||||
# 使用TODO注释标记技术债务
|
||||
# TODO: 重构这个函数,职责过多
|
||||
# FIXME: 这里有性能问题,需要优化
|
||||
# HACK: 临时解决方案,需要找到更好的方法
|
||||
# NOTE: 这里的逻辑比较复杂,需要详细注释
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**记住:优秀的代码不仅能工作,更要易于理解、维护和扩展。追求代码质量是每个开发者的责任!**
|
||||
272
docs/02-开发规范/架构与通信规范.md
Normal file
272
docs/02-开发规范/架构与通信规范.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# 架构与通信规范
|
||||
|
||||
本文档定义了WhaleTown项目的架构设计原则和组件间通信规范。
|
||||
|
||||
## 🏛️ 架构设计原则
|
||||
|
||||
### 核心原则
|
||||
- **"信号向上,调用向下"** - 父节点调用子节点方法,子节点发出信号通知父节点
|
||||
- **高度解耦** - 通过事件系统实现组件间通信,避免直接依赖
|
||||
- **分层架构** - 严格的三层架构:框架层、游戏层、界面层
|
||||
- **单一职责** - 每个组件只负责一个明确的功能
|
||||
|
||||
### 分层架构详解
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ UI Layer (界面层) │
|
||||
│ UI/Windows/, UI/HUD/ │
|
||||
├─────────────────────────────────────┤
|
||||
│ Scenes Layer (游戏层) │
|
||||
│ Scenes/Maps/, Scenes/Entities/ │
|
||||
├─────────────────────────────────────┤
|
||||
│ _Core Layer (框架层) │
|
||||
│ _Core/managers/, _Core/systems/ │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 🔄 事件系统 (EventSystem)
|
||||
|
||||
### 事件系统位置
|
||||
- **文件路径**: `_Core/systems/EventSystem.gd`
|
||||
- **自动加载**: 必须设置为AutoLoad单例
|
||||
- **作用**: 全局事件总线,实现跨模块通信
|
||||
|
||||
### 事件命名规范
|
||||
所有事件名称必须在 `_Core/EventNames.gd` 中定义:
|
||||
|
||||
```gdscript
|
||||
# _Core/EventNames.gd
|
||||
class_name EventNames
|
||||
|
||||
# 玩家相关事件
|
||||
const PLAYER_MOVED = "player_moved"
|
||||
const PLAYER_HEALTH_CHANGED = "player_health_changed"
|
||||
const PLAYER_DIED = "player_died"
|
||||
|
||||
# 交互事件
|
||||
const INTERACT_PRESSED = "interact_pressed"
|
||||
const NPC_TALKED = "npc_talked"
|
||||
const ITEM_COLLECTED = "item_collected"
|
||||
|
||||
# UI事件
|
||||
const UI_BUTTON_CLICKED = "ui_button_clicked"
|
||||
const DIALOG_OPENED = "dialog_opened"
|
||||
const DIALOG_CLOSED = "dialog_closed"
|
||||
|
||||
# 游戏状态事件
|
||||
const GAME_PAUSED = "game_paused"
|
||||
const GAME_RESUMED = "game_resumed"
|
||||
const SCENE_CHANGED = "scene_changed"
|
||||
```
|
||||
|
||||
### 事件使用方法
|
||||
|
||||
#### 发送事件
|
||||
```gdscript
|
||||
# 发送简单事件
|
||||
EventSystem.emit_event(EventNames.PLAYER_MOVED)
|
||||
|
||||
# 发送带数据的事件
|
||||
EventSystem.emit_event(EventNames.PLAYER_HEALTH_CHANGED, {
|
||||
"old_health": 80,
|
||||
"new_health": 60,
|
||||
"damage": 20
|
||||
})
|
||||
```
|
||||
|
||||
#### 监听事件
|
||||
```gdscript
|
||||
func _ready() -> void:
|
||||
# 连接事件监听
|
||||
EventSystem.connect_event(EventNames.PLAYER_DIED, _on_player_died)
|
||||
EventSystem.connect_event(EventNames.ITEM_COLLECTED, _on_item_collected)
|
||||
|
||||
func _on_player_died(data: Dictionary = {}) -> void:
|
||||
print("玩家死亡,游戏结束")
|
||||
# 处理玩家死亡逻辑
|
||||
|
||||
func _on_item_collected(data: Dictionary) -> void:
|
||||
var item_name = data.get("item_name", "未知物品")
|
||||
print("收集到物品: ", item_name)
|
||||
```
|
||||
|
||||
#### 断开事件监听
|
||||
```gdscript
|
||||
func _exit_tree() -> void:
|
||||
# 节点销毁时断开事件监听
|
||||
EventSystem.disconnect_event(EventNames.PLAYER_DIED, _on_player_died)
|
||||
EventSystem.disconnect_event(EventNames.ITEM_COLLECTED, _on_item_collected)
|
||||
```
|
||||
|
||||
## 🎯 单例管理器
|
||||
|
||||
### 允许的自动加载单例
|
||||
项目中只允许以下三个单例:
|
||||
|
||||
1. **GameManager** - 游戏状态管理
|
||||
- 路径: `_Core/managers/GameManager.gd`
|
||||
- 职责: 游戏状态、场景数据、全局配置
|
||||
|
||||
2. **SceneManager** - 场景管理
|
||||
- 路径: `_Core/managers/SceneManager.gd`
|
||||
- 职责: 场景切换、场景生命周期
|
||||
|
||||
3. **EventSystem** - 事件系统
|
||||
- 路径: `_Core/systems/EventSystem.gd`
|
||||
- 职责: 全局事件通信
|
||||
|
||||
### 单例使用规范
|
||||
```gdscript
|
||||
# ✅ 正确:高层组件可以访问单例
|
||||
func _ready() -> void:
|
||||
var current_scene = SceneManager.get_current_scene()
|
||||
var game_state = GameManager.get_game_state()
|
||||
|
||||
# ❌ 错误:底层实体不应直接访问GameManager
|
||||
# 在Player.gd或NPC.gd中避免这样做:
|
||||
func _ready() -> void:
|
||||
GameManager.register_player(self) # 不推荐
|
||||
|
||||
# ✅ 正确:使用事件系统
|
||||
func _ready() -> void:
|
||||
EventSystem.emit_event(EventNames.PLAYER_SPAWNED, {"player": self})
|
||||
```
|
||||
|
||||
## 🔗 组件通信模式
|
||||
|
||||
### 1. 父子通信
|
||||
```gdscript
|
||||
# 父节点调用子节点方法(向下调用)
|
||||
func _on_button_pressed() -> void:
|
||||
child_component.activate()
|
||||
child_component.set_data(some_data)
|
||||
|
||||
# 子节点发出信号通知父节点(向上信号)
|
||||
# 在子节点中:
|
||||
signal component_activated(data: Dictionary)
|
||||
signal component_finished()
|
||||
|
||||
func _some_action() -> void:
|
||||
component_activated.emit({"status": "active"})
|
||||
```
|
||||
|
||||
### 2. 兄弟组件通信
|
||||
```gdscript
|
||||
# 通过共同的父节点中转
|
||||
# 或使用事件系统
|
||||
func _notify_sibling() -> void:
|
||||
EventSystem.emit_event(EventNames.COMPONENT_MESSAGE, {
|
||||
"sender": self,
|
||||
"message": "Hello sibling!"
|
||||
})
|
||||
```
|
||||
|
||||
### 3. 跨场景通信
|
||||
```gdscript
|
||||
# 使用事件系统进行跨场景通信
|
||||
func _change_scene_with_data() -> void:
|
||||
EventSystem.emit_event(EventNames.SCENE_DATA_TRANSFER, {
|
||||
"target_scene": "battle_scene",
|
||||
"player_data": player_data
|
||||
})
|
||||
```
|
||||
|
||||
## 🚫 禁止的通信模式
|
||||
|
||||
### 1. 直接节点引用
|
||||
```gdscript
|
||||
# ❌ 错误:直接获取其他场景的节点
|
||||
func _bad_communication() -> void:
|
||||
var other_scene = get_tree().get_first_node_in_group("other_scene")
|
||||
other_scene.do_something() # 强耦合,难以维护
|
||||
```
|
||||
|
||||
### 2. 全局变量传递
|
||||
```gdscript
|
||||
# ❌ 错误:使用全局变量传递状态
|
||||
# 在autoload中:
|
||||
var global_player_data = {} # 避免这种做法
|
||||
```
|
||||
|
||||
### 3. 循环依赖
|
||||
```gdscript
|
||||
# ❌ 错误:A依赖B,B又依赖A
|
||||
# ComponentA.gd
|
||||
var component_b: ComponentB
|
||||
|
||||
# ComponentB.gd
|
||||
var component_a: ComponentA # 循环依赖
|
||||
```
|
||||
|
||||
## 📋 通信最佳实践
|
||||
|
||||
### 1. 事件数据结构
|
||||
```gdscript
|
||||
# 使用结构化的事件数据
|
||||
EventSystem.emit_event(EventNames.PLAYER_ATTACK, {
|
||||
"attacker": self,
|
||||
"target": target_enemy,
|
||||
"damage": damage_amount,
|
||||
"attack_type": "melee",
|
||||
"timestamp": Time.get_time_dict_from_system()
|
||||
})
|
||||
```
|
||||
|
||||
### 2. 错误处理
|
||||
```gdscript
|
||||
func _on_event_received(data: Dictionary) -> void:
|
||||
# 验证数据完整性
|
||||
if not data.has("required_field"):
|
||||
push_error("事件数据缺少必需字段: required_field")
|
||||
return
|
||||
|
||||
# 安全地获取数据
|
||||
var value = data.get("optional_field", default_value)
|
||||
```
|
||||
|
||||
### 3. 性能考虑
|
||||
```gdscript
|
||||
# 避免在_process中频繁发送事件
|
||||
var last_position: Vector2
|
||||
func _process(delta: float) -> void:
|
||||
if global_position.distance_to(last_position) > 10.0:
|
||||
EventSystem.emit_event(EventNames.PLAYER_MOVED, {
|
||||
"position": global_position
|
||||
})
|
||||
last_position = global_position
|
||||
```
|
||||
|
||||
## 🧪 测试通信系统
|
||||
|
||||
### 单元测试示例
|
||||
```gdscript
|
||||
extends GutTest
|
||||
|
||||
func test_event_emission():
|
||||
# 监听事件
|
||||
watch_signals(EventSystem)
|
||||
|
||||
# 发送事件
|
||||
EventSystem.emit_event(EventNames.PLAYER_MOVED, {"x": 100, "y": 200})
|
||||
|
||||
# 验证事件发送
|
||||
assert_signal_emitted(EventSystem, "event_raised")
|
||||
|
||||
func test_event_data():
|
||||
var received_data: Dictionary
|
||||
|
||||
# 连接事件监听
|
||||
EventSystem.connect_event(EventNames.TEST_EVENT, func(data): received_data = data)
|
||||
|
||||
# 发送测试数据
|
||||
var test_data = {"test": "value"}
|
||||
EventSystem.emit_event(EventNames.TEST_EVENT, test_data)
|
||||
|
||||
# 验证数据传递
|
||||
assert_eq(received_data, test_data)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**记住:良好的架构设计是项目成功的基石!遵循这些通信规范可以确保代码的可维护性和扩展性。**
|
||||
Reference in New Issue
Block a user