docs: 重新组织文档结构,按开发阶段分类

新的目录结构:
  01-项目入门/     # 新人必读,项目基础
  02-开发规范/     # 编码标准和规范
  03-技术实现/     # 具体开发指导
  04-高级开发/     # 进阶开发技巧
  05-部署运维/     # 发布和部署
  06-功能模块/     # 特定功能文档

 新增导航文档:
- docs/README.md - 完整的文档导航和使用指南
- 各目录下的README.md - 分类说明和使用指导

 优化效果:
- 开发者可以按阶段快速定位需要的文档
- 新人有清晰的学习路径
- 不同角色有针对性的文档推荐
- 提供了问题导向的快速查找功能
This commit is contained in:
2025-12-31 18:02:16 +08:00
parent 2998fd2d11
commit 1ff677b3b2
22 changed files with 449 additions and 0 deletions

View 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 混在一起
**好的提交习惯 = 清晰的项目历史 = 高效的团队协作**

View 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提交改进方案
---
**记住:规范不是束缚,而是团队协作的基础!**

View 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提供更好的上下文信息
记住:**好的注释解释为什么,而不是做什么。**

View 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`
保持一致,代码更清晰!

View 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: 这里的逻辑比较复杂,需要详细注释
```
---
**记住:优秀的代码不仅能工作,更要易于理解、维护和扩展。追求代码质量是每个开发者的责任!**

View 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依赖BB又依赖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)
```
---
**记住:良好的架构设计是项目成功的基石!遵循这些通信规范可以确保代码的可维护性和扩展性。**