369 lines
7.7 KiB
Markdown
369 lines
7.7 KiB
Markdown
# 代码风格指南
|
|
|
|
## 概述
|
|
|
|
本文档定义了 AI Town Game 项目的代码风格和最佳实践。遵循这些规范有助于保持代码的一致性、可读性和可维护性。
|
|
|
|
## 命名规范
|
|
|
|
### 变量和函数
|
|
- 使用 `snake_case` 命名法
|
|
- 变量名应该描述性强,避免缩写
|
|
- 布尔变量使用 `is_`, `has_`, `can_` 等前缀
|
|
|
|
```gdscript
|
|
# 好的例子
|
|
var player_character: CharacterController
|
|
var is_connected: bool
|
|
var has_valid_data: bool
|
|
|
|
# 避免的例子
|
|
var pc: CharacterController
|
|
var conn: bool
|
|
var data: bool
|
|
```
|
|
|
|
### 类和枚举
|
|
- 使用 `PascalCase` 命名法
|
|
- 类名应该是名词
|
|
- 枚举值使用 `UPPER_CASE`
|
|
|
|
```gdscript
|
|
# 类名
|
|
class_name NetworkManager
|
|
class_name CharacterController
|
|
|
|
# 枚举
|
|
enum GameState {
|
|
LOGIN,
|
|
CHARACTER_CREATION,
|
|
IN_GAME
|
|
}
|
|
```
|
|
|
|
### 常量
|
|
- 使用 `UPPER_CASE` 命名法
|
|
- 相关常量可以组织在字典中
|
|
|
|
```gdscript
|
|
const MAX_PLAYERS = 50
|
|
const DEFAULT_TIMEOUT = 10.0
|
|
|
|
const COLORS = {
|
|
"online": Color.GREEN,
|
|
"offline": Color.GRAY
|
|
}
|
|
```
|
|
|
|
### 信号
|
|
- 使用 `snake_case` 命名法
|
|
- 使用过去时态描述已发生的事件
|
|
|
|
```gdscript
|
|
signal character_spawned(character_id: String)
|
|
signal connection_established()
|
|
signal message_received(data: Dictionary)
|
|
```
|
|
|
|
## 代码组织
|
|
|
|
### 文件结构
|
|
每个脚本文件应该按以下顺序组织:
|
|
|
|
1. `extends` 和 `class_name` 声明
|
|
2. 类文档注释
|
|
3. 信号定义
|
|
4. 常量定义
|
|
5. 导出变量
|
|
6. 公共变量
|
|
7. 私有变量
|
|
8. 内置函数(`_ready`, `_process` 等)
|
|
9. 公共函数
|
|
10. 私有函数
|
|
|
|
```gdscript
|
|
extends Node
|
|
class_name ExampleClass
|
|
## 示例类
|
|
## 展示代码组织结构
|
|
|
|
# 信号
|
|
signal data_changed(new_data: Dictionary)
|
|
|
|
# 常量
|
|
const MAX_ITEMS = 100
|
|
|
|
# 导出变量
|
|
@export var item_count: int = 0
|
|
|
|
# 公共变量
|
|
var current_state: GameState
|
|
|
|
# 私有变量
|
|
var _internal_data: Dictionary = {}
|
|
|
|
func _ready():
|
|
"""初始化函数"""
|
|
pass
|
|
|
|
func public_function() -> void:
|
|
"""公共函数"""
|
|
pass
|
|
|
|
func _private_function() -> void:
|
|
"""私有函数"""
|
|
pass
|
|
```
|
|
|
|
### 函数组织
|
|
- 相关功能的函数应该放在一起
|
|
- 使用注释分隔不同的功能区域
|
|
- 私有函数以下划线开头
|
|
|
|
```gdscript
|
|
## === 网络相关函数 ===
|
|
|
|
func connect_to_server() -> void:
|
|
"""连接到服务器"""
|
|
pass
|
|
|
|
func disconnect_from_server() -> void:
|
|
"""断开服务器连接"""
|
|
pass
|
|
|
|
func _handle_network_error() -> void:
|
|
"""处理网络错误(私有函数)"""
|
|
pass
|
|
|
|
## === UI相关函数 ===
|
|
|
|
func show_notification() -> void:
|
|
"""显示通知"""
|
|
pass
|
|
```
|
|
|
|
## 注释和文档
|
|
|
|
### 类文档
|
|
每个类都应该有文档注释,说明其用途和职责:
|
|
|
|
```gdscript
|
|
extends Node
|
|
class_name NetworkManager
|
|
## 网络管理器
|
|
## 负责管理客户端与服务器的 WebSocket 连接
|
|
##
|
|
## 主要功能:
|
|
## - 建立和维护网络连接
|
|
## - 处理消息收发
|
|
## - 管理重连逻辑
|
|
```
|
|
|
|
### 函数文档
|
|
公共函数应该有详细的文档注释:
|
|
|
|
```gdscript
|
|
func spawn_character(character_data: Dictionary, is_player: bool = false) -> CharacterController:
|
|
"""
|
|
在世界中生成角色
|
|
|
|
@param character_data: 角色数据字典,必须包含 id 和 name 字段
|
|
@param is_player: 是否为玩家角色,默认为 false
|
|
@return: 生成的角色控制器实例,失败时返回 null
|
|
|
|
示例:
|
|
var data = {"id": "char_123", "name": "Hero"}
|
|
var character = spawn_character(data, true)
|
|
"""
|
|
pass
|
|
```
|
|
|
|
### 行内注释
|
|
- 解释复杂的逻辑
|
|
- 说明为什么这样做,而不是做了什么
|
|
- 避免显而易见的注释
|
|
|
|
```gdscript
|
|
# 好的注释 - 解释原因
|
|
# 使用指数退避算法避免服务器过载
|
|
_reconnect_timer = _reconnect_delay * pow(2, _reconnect_attempts)
|
|
|
|
# 避免的注释 - 重复代码
|
|
# 设置重连计时器为延迟时间
|
|
_reconnect_timer = _reconnect_delay
|
|
```
|
|
|
|
## 错误处理
|
|
|
|
### 使用统一的错误处理
|
|
使用项目的 `ErrorHandler` 类记录错误:
|
|
|
|
```gdscript
|
|
# 记录网络错误
|
|
ErrorHandler.log_network_error("Connection failed", {"url": server_url})
|
|
|
|
# 记录游戏逻辑错误
|
|
ErrorHandler.log_game_error("Invalid character data", {"character_id": char_id})
|
|
```
|
|
|
|
### 防御性编程
|
|
- 验证输入参数
|
|
- 检查空引用
|
|
- 处理边界情况
|
|
|
|
```gdscript
|
|
func update_character_position(character_id: String, position: Vector2) -> void:
|
|
"""更新角色位置"""
|
|
# 验证输入
|
|
if character_id.is_empty():
|
|
ErrorHandler.log_game_error("Empty character ID provided")
|
|
return
|
|
|
|
# 检查角色是否存在
|
|
if not characters.has(character_id):
|
|
ErrorHandler.log_game_error("Character not found", {"id": character_id})
|
|
return
|
|
|
|
# 执行更新
|
|
var character = characters[character_id]
|
|
character.set_position_smooth(position)
|
|
```
|
|
|
|
## 性能最佳实践
|
|
|
|
### 避免不必要的计算
|
|
```gdscript
|
|
# 好的做法 - 缓存计算结果
|
|
var distance_squared = position.distance_squared_to(target)
|
|
if distance_squared < interaction_range_squared:
|
|
# 执行交互
|
|
|
|
# 避免的做法 - 重复计算
|
|
if position.distance_to(target) < interaction_range:
|
|
# 执行交互
|
|
```
|
|
|
|
### 使用对象池
|
|
对于频繁创建和销毁的对象,考虑使用对象池:
|
|
|
|
```gdscript
|
|
# 从对象池获取对象而不是创建新对象
|
|
var bullet = BulletPool.get_bullet()
|
|
bullet.initialize(position, direction)
|
|
```
|
|
|
|
### 合理使用信号
|
|
- 避免在每帧都发射信号
|
|
- 使用一次性连接(`connect(..., CONNECT_ONE_SHOT)`)当适用时
|
|
|
|
## 测试
|
|
|
|
### 测试命名
|
|
测试函数应该清楚地描述测试内容:
|
|
|
|
```gdscript
|
|
func test_character_creation_with_valid_data():
|
|
"""测试使用有效数据创建角色"""
|
|
pass
|
|
|
|
func test_network_connection_timeout():
|
|
"""测试网络连接超时处理"""
|
|
pass
|
|
```
|
|
|
|
### 属性测试标注
|
|
属性测试必须包含特定的注释格式:
|
|
|
|
```gdscript
|
|
## Feature: godot-ai-town-game, Property 1: 角色创建唯一性
|
|
func test_property_character_id_uniqueness():
|
|
"""
|
|
属性测试:角色创建唯一性
|
|
对于任意两个成功创建的角色,它们的角色 ID 应该是唯一的
|
|
"""
|
|
pass
|
|
```
|
|
|
|
## 配置管理
|
|
|
|
### 使用配置类
|
|
避免硬编码常量,使用 `GameConfig` 类:
|
|
|
|
```gdscript
|
|
# 好的做法
|
|
var move_speed = GameConfig.get_character_move_speed()
|
|
var timeout = GameConfig.NETWORK.connection_timeout
|
|
|
|
# 避免的做法
|
|
var move_speed = 200.0
|
|
var timeout = 10.0
|
|
```
|
|
|
|
## Git 提交规范
|
|
|
|
### 提交消息格式
|
|
```
|
|
<type>(<scope>): <description>
|
|
|
|
[optional body]
|
|
|
|
[optional footer]
|
|
```
|
|
|
|
### 类型
|
|
- `feat`: 新功能
|
|
- `fix`: 修复bug
|
|
- `refactor`: 重构代码
|
|
- `docs`: 文档更新
|
|
- `test`: 测试相关
|
|
- `style`: 代码格式调整
|
|
- `perf`: 性能优化
|
|
|
|
### 示例
|
|
```
|
|
feat(network): add automatic reconnection logic
|
|
|
|
Implement exponential backoff algorithm for reconnection attempts.
|
|
Maximum 3 attempts with increasing delay between attempts.
|
|
|
|
Closes #123
|
|
```
|
|
|
|
## 工具使用
|
|
|
|
### 使用工具类
|
|
项目提供了多个工具类,应该优先使用:
|
|
|
|
```gdscript
|
|
# 使用 Utils 类的工具函数
|
|
if Utils.is_string_blank(character_name):
|
|
return false
|
|
|
|
var unique_id = Utils.generate_unique_id("char_")
|
|
var label = Utils.create_label_with_shadow("Player Name")
|
|
|
|
# 使用深度比较
|
|
if Utils.deep_equals(data1, data2):
|
|
# 数据相同
|
|
```
|
|
|
|
### 性能监控
|
|
在关键路径上使用性能监控:
|
|
|
|
```gdscript
|
|
# 记录网络延迟
|
|
var start_time = Time.get_ticks_msec()
|
|
# ... 网络操作 ...
|
|
var latency = Time.get_ticks_msec() - start_time
|
|
PerformanceMonitor.record_network_latency(latency)
|
|
```
|
|
|
|
## 总结
|
|
|
|
遵循这些代码风格指南将有助于:
|
|
- 提高代码可读性和可维护性
|
|
- 减少bug和错误
|
|
- 提升团队协作效率
|
|
- 保持项目的长期健康发展
|
|
|
|
所有团队成员都应该熟悉并遵循这些规范。在代码审查时,这些规范也是重要的检查点。 |