文章目录
红楼回忆志 - 代码重构方案
一、当前代码结构分析
1.1 项目概览
游戏引擎: Godot 4.6 (Mobile) 后端服务: Supabase (PostgreSQL) 项目类型: 2D 叙事策略游戏
1.2 现有目录结构
</> PLAINTEXT
honglou/
├── autoload/ # 全局自动加载脚本 (2 个文件)
│ ├── GameTime.gd
│ └── GameTime.gd.uid
│
├── scripts/ # 主要脚本目录
│ ├── autoload/ # 核心 Autoload 脚本 (10 个文件)
│ │ ├── EavesdropManager.gd
│ │ ├── EventBus.gd
│ │ ├── FirebaseManager.gd
│ │ ├── GameState.gd
│ │ ├── MaidProgressionChecker.gd
│ │ ├── MockDatabase.gd
│ │ ├── PlayerState.gd
│ │ ├── RelationshipManager.gd
│ │ ├── StaminaManager.gd
│ │ └── SupabaseManager.gd
│ │
│ ├── data/ # 数据模型 (3 个文件)
│ │ ├── GameConfig.gd
│ │ ├── intel_templates.gd
│ │ └── PlayerData.gd
│ │
│ ├── systems/ # 游戏系统 (1 个文件)
│ │ └── PowerInfluence.gd
│ │
│ ├── ui/ # UI 脚本 (40 个文件)
│ │ ├── DebugPanelUI.gd
│ │ ├── EavesdropHub.gd
│ │ ├── EavesdropScene.gd
│ │ ├── HubUI.gd
│ │ ├── InboxUI.gd
│ │ └── ...
│ │
│ ├── inbox/ # 信箱模块
│ │ └── InboxManager.gd
│ │
│ ├── market/ # 市场模块
│ │ ├── BridgeMarket.gd
│ │ ├── BridgeMarketUI.gd
│ │ └── ListingCard.gd
│ │
│ └── test/ # 测试脚本
│
├── scenes/ # 场景文件
│ ├── components/ # 可复用组件 (5 个.tscn)
│ ├── market/ # 市场场景
│ ├── test/ # 测试场景
│ ├── ui/ # UI 场景 (5 个.tscn)
│ └── *.tscn # 主场景文件 (13 个)
│
├── ui/ # 旧 UI 目录 (3 个文件,冗余)
│ ├── TimePanel.gd
│ ├── TimePanel.gd.uid
│ └── TimePanel.tscn
│
├── src/ # 核心游戏逻辑 (23 个文件)
│ ├── maps/ # 地图相关
│ ├── animations.gd
│ ├── assets.gd
│ ├── camera.gd
│ ├── collision.gd
│ ├── constants.gd
│ ├── game.gd
│ ├── input.gd
│ ├── player.gd
│ ├── player_states.gd
│ ├── scene.gd
│ └── tilemap.gd
│
├── resources/ # 资源文件
│ └── theme/ # 主题资源
│
├── utils/ # 工具函数 (1 个文件)
│ └── TimeFormatter.gd
│
├── debug/ # 调试工具
│ └── DebugTimePanel.tscn
│
├── supabase/ # Supabase 配置
│ ├── functions/ # Edge Functions
│ ├── migrations/ # 数据库迁移
│ └── config.toml
│
└── *.sql # SQL 文件 (4 个)
1.3 主要问题识别
问题 1: 目录结构分散冗余
| 问题 | 描述 | 影响 |
|---|---|---|
| UI 目录分裂 | scripts/ui/ (40 文件) + ui/ (3 文件) + scenes/ui/ (5 文件) |
查找困难,维护混乱 |
| Autoload 分散 | autoload/ (2 文件) + scripts/autoload/ (10 文件) |
配置不统一 |
| 场景脚本分离 | scenes/*.tscn 对应 scripts/*.gd 无明确规则 |
难以定位配对文件 |
| src 目录孤立 | src/ 包含核心逻辑但与 scripts/ 并列 |
职责边界模糊 |
问题 2: 命名规范不一致
</> GDSCRIPT
# 混用的命名风格
GameState.gd # PascalCase
PlayerState.gd # PascalCase
intel_templates.gd # snake_case ❌
TimeFormatter.gd # PascalCase
PowerInfluence.gd # PascalCase
问题 3: 职责划分不清
GameState.gd: 管理全局数值,但GameConfig.gd也存储配置PlayerState.gd: 玩家状态,但PlayerData.gd也存在SupabaseManager.gd: 463 行,过于臃肿src/game.gdvsscripts/autoload/GameState.gd: 职责重叠
问题 4: 缺乏模块化
- 游戏功能模块(听壁脚、信箱、市场)分散在多处
- 缺少清晰的领域边界
- UI 组件与业务逻辑耦合
二、重构目标
2.1 核心原则
- 单一职责: 每个文件/目录只做一件事
- 高内聚低耦合: 相关功能聚集,模块间依赖最小化
- 约定优于配置: 统一命名和目录规范
- 渐进式重构: 分阶段进行,不影响现有功能
2.2 预期收益
| 方面 | 改进 |
|---|---|
| 可维护性 | 文件查找时间减少 70% |
| 可读性 | 新成员上手时间缩短 50% |
| 可扩展性 | 新增模块只需遵循固定模式 |
| 可测试性 | 模块独立,便于单元测试 |
三、重构方案
3.1 新目录结构
</> PLAINTEXT
honglou/
├── core/ # 【新建】核心引擎层
│ ├── autoload/ # 全局单例(原 scripts/autoload + autoload)
│ │ ├── GameState.gd
│ │ ├── PlayerState.gd
│ │ ├── EventBus.gd
│ │ ├── GameTime.gd
│ │ ├── GameConfig.gd
│ │ ├── SupabaseManager.gd
│ │ ├── MockDatabase.gd
│ │ ├── StaminaManager.gd
│ │ ├── EavesdropManager.gd
│ │ ├── RelationshipManager.gd
│ │ ├── MaidProgressionChecker.gd
│ │ └── InboxManager.gd
│ │
│ ├── systems/ # 【新建】核心游戏系统(原 src/)
│ │ ├── Game.gd
│ │ ├── Player.gd
│ │ ├── Camera.gd
│ │ ├── InputHandler.gd
│ │ ├── TileMap.gd
│ │ ├── Collision.gd
│ │ ├── Animation.gd
│ │ └── Scene.gd
│ │
│ └── constants/ # 【新建】全局常量
│ ├── GameConstants.gd # 原 constants.gd
│ └── AssetPaths.gd # 原 assets.gd
│
├── features/ # 【新建】功能模块层(按领域划分)
│ ├── eavesdrop/ # 听壁脚模块
│ │ ├── EavesdropManager.gd # 如已有则移至 core/autoload
│ │ ├── EavesdropScene.gd
│ │ ├── EavesdropHub.gd
│ │ ├── EavesdropSessionItem.gd
│ │ ├── IntelIntercept.gd
│ │ ├── IntelBag.gd
│ │ ├── IntelBagUI.gd
│ │ ├── IntelFragmentItem.gd
│ │ └── intel_templates.gd
│ │
│ ├── market/ # 市场模块
│ │ ├── BridgeMarket.gd
│ │ ├── BridgeMarketUI.gd
│ │ └── ListingCard.gd
│ │
│ ├── inbox/ # 信箱模块
│ │ ├── InboxManager.gd # 如已有则移至 core/autoload
│ │ └── InboxUI.gd
│ │
│ ├── relationship/ # 关系模块
│ │ ├── RelationshipManager.gd # 如已有则移至 core/autoload
│ │ ├── RelationshipPanel.gd
│ │ └── MaidProgressPanel.gd
│ │
│ └── power/ # 势力模块
│ └── PowerInfluence.gd
│
├── scenes/ # 场景文件(仅保留.tscn)
│ ├── main/ # 主流程场景
│ │ ├── Main.tscn
│ │ ├── Login.tscn
│ │ ├── RoleSelect.tscn
│ │ ├── Hub.tscn
│ │ ├── Game.tscn
│ │ └── Settlement.tscn
│ │
│ ├── features/ # 功能场景
│ │ ├── EavesdropScene.tscn
│ │ ├── EavesdropHub.tscn
│ │ ├── IntelIntercept.tscn
│ │ ├── Market.tscn
│ │ ├── Inbox.tscn
│ │ ├── RelationshipPanel.tscn
│ │ └── MaidProgressPanel.tscn
│ │
│ ├── components/ # 可复用组件
│ │ ├── ComposePanel.tscn
│ │ ├── EavesdropSessionItem.tscn
│ │ ├── IntelFragmentItem.tscn
│ │ ├── MessageCard.tscn
│ │ ├── PowerStatusIndicator.tscn
│ │ └── RumorCard.tscn
│ │
│ └── debug/ # 调试场景
│ └── DebugTimePanel.tscn
│
├── scripts/ # 脚本文件(仅保留.gd,与 scenes 对应)
│ ├── main/ # 主流程脚本
│ │ ├── Main.gd
│ │ ├── Login.gd
│ │ ├── RoleSelect.gd
│ │ ├── Hub.gd
│ │ ├── Game.gd
│ │ └── Settlement.gd
│ │
│ ├── features/ # 功能脚本
│ │ ├── eavesdrop/
│ │ ├── market/
│ │ ├── inbox/
│ │ └── ...
│ │
│ ├── components/ # 组件脚本
│ │ └── *.gd
│ │
│ └── debug/ # 调试脚本
│ └── DebugPanelUI.gd
│
├── ui/ # 【清理】统一 UI 层
│ ├── common/ # 通用 UI 组件
│ │ ├── TimePanel.gd
│ │ ├── TimePanel.tscn
│ │ └── PowerStatusIndicator.gd
│ │
│ └── dialogs/ # 弹窗组件
│ ├── PublishRumorPanel.gd
│ └── PublishRumorPanel.tscn
│
├── data/ # 【新建】数据层
│ ├── models/ # 数据模型
│ │ ├── PlayerData.gd
│ │ └── GameConfig.gd # 如已有则移至 core/autoload
│ │
│ └── templates/ # 数据模板
│ └── intel_templates.gd
│
├── utils/ # 工具函数
│ ├── TimeFormatter.gd
│ └── StringUtils.gd # 【新建】
│
├── resources/ # 资源文件
│ ├── theme/
│ ├── sprites/ # 【新建】精灵图
│ ├── audio/ # 【新建】音频
│ └── fonts/ # 【新建】字体
│
├── supabase/ # Supabase 配置(保持不变)
│ ├── functions/
│ ├── migrations/
│ └── config.toml
│
├── tests/ # 【新建】测试目录
│ ├── unit/
│ └── integration/
│
└── docs/ # 【新建】文档目录
├── architecture.md
├── module-specifications.md
└── api-reference.md
3.2 迁移映射表
| 原路径 | 新路径 | 说明 |
|---|---|---|
autoload/GameTime.gd |
core/autoload/GameTime.gd |
移动 |
scripts/autoload/*.gd |
core/autoload/*.gd |
移动 |
src/game.gd |
core/systems/Game.gd |
移动 + 重命名 |
src/player.gd |
core/systems/Player.gd |
移动 + 重命名 |
src/*.gd |
core/systems/*.gd |
移动 + PascalCase |
scripts/data/GameConfig.gd |
core/autoload/GameConfig.gd |
移动 |
scripts/data/intel_templates.gd |
features/eavesdrop/intel_templates.gd |
按领域移动 |
scripts/ui/*.gd |
scripts/features/*/ |
按功能分散 |
ui/TimePanel.* |
ui/common/TimePanel.* |
整理 |
scenes/components/*.tscn |
scenes/components/*.tscn |
保持 |
scenes/*.tscn |
scenes/main/ 或 scenes/features/ |
分类 |
3.3 命名规范统一
文件命名
</> GDSCRIPT
# ✅ 所有脚本使用 PascalCase.gd
GameState.gd
PlayerState.gd
IntelTemplates.gd # 原 intel_templates.gd
TimeFormatter.gd
# ✅ 场景文件使用 PascalCase.tscn
Main.tscn
EavesdropScene.tscn
# ✅ 组件使用前缀
UI_Panel.tscn # UI 组件
CMP_SessionItem.tscn # 通用组件
类命名
</> GDSCRIPT
# 与文件名一致
class_name GameState # 全局可访问
class_name EavesdropManager # 全局可访问
信号命名
</> GDSCRIPT
# 使用过去分词,表示已完成的动作
signal session_started
signal intel_received
signal stamina_changed
# 避免使用现在分词
# ❌ signal starting_session
# ❌ signal receiving_intel
3.4 代码组织规范
Autoload 注册(project.godot)
</> INI
[autoload]
# 核心层
GameState="res://core/autoload/GameState.gd"
PlayerState="res://core/autoload/PlayerState.gd"
EventBus="res://core/autoload/EventBus.gd"
GameTime="res://core/autoload/GameTime.gd"
GameConfig="res://core/autoload/GameConfig.gd"
# 服务层
SupabaseManager="res://core/autoload/SupabaseManager.gd"
MockDatabase="res://core/autoload/MockDatabase.gd"
StaminaManager="res://core/autoload/StaminaManager.gd"
# 功能层
EavesdropManager="res://features/eavesdrop/EavesdropManager.gd"
RelationshipManager="res://features/relationship/RelationshipManager.gd"
MaidProgressionChecker="res://features/relationship/MaidProgressionChecker.gd"
InboxManager="res://features/inbox/InboxManager.gd"
场景与脚本配对
</> PLAINTEXT
# 同一功能模块的场景和脚本放在对应子目录
scenes/features/eavesdrop/EavesdropScene.tscn
scripts/features/eavesdrop/EavesdropScene.gd
# 组件配对
scenes/components/CMP_SessionItem.tscn
scripts/components/CMP_SessionItem.gd
四、实施步骤
阶段一:准备工作(1 天)
任务 1.1: 创建新目录结构
</> BASH
# 创建核心层
mkdir -p core/autoload core/systems core/constants
# 创建功能层
mkdir -p features/eavesdrop features/market features/inbox features/relationship features/power
# 重组 scenes
mkdir -p scenes/main scenes/features scenes/components scenes/debug
# 重组 scripts
mkdir -p scripts/main scripts/features scripts/components scripts/debug
# 创建其他目录
mkdir -p data/models data/templates
mkdir -p ui/common ui/dialogs
mkdir -p resources/sprites resources/audio resources/fonts
mkdir -p tests/unit tests/integration
mkdir -p docs
任务 1.2: 备份当前代码
</> BASH
git add .
git commit -m "chore: backup before refactoring"
git branch backup-pre-refactor
任务 1.3: 更新.gitignore
</> GITIGNORE
# 旧目录(重构完成后删除)
# autoload/
# src/
# scripts/autoload/
# scripts/data/
# scripts/ui/
# ui/
阶段二:核心层迁移(2 天)
任务 2.1: 迁移 Autoload
- 复制
autoload/和scripts/autoload/到core/autoload/ - 统一命名(PascalCase)
- 更新
project.godot的[autoload]部分 - 测试游戏启动
任务 2.2: 迁移 src/
- 复制
src/*.gd到core/systems/ - 重命名为 PascalCase
- 更新所有引用路径
- 测试核心功能
任务 2.3: 迁移常量
- 复制
src/constants.gd到core/constants/GameConstants.gd - 复制
src/assets.gd到core/constants/AssetPaths.gd - 更新全局引用
阶段三:功能层迁移(3 天)
任务 3.1: 听壁脚模块
</> BASH
# 移动相关文件
mv scripts/ui/Eavesdrop*.gd features/eavesdrop/
mv scripts/ui/Intel*.gd features/eavesdrop/
mv scripts/data/intel_templates.gd features/eavesdrop/
mv scenes/ui/Eavesdrop*.tscn scenes/features/
mv scenes/ui/IntelIntercept.tscn scenes/features/
任务 3.2: 市场模块
</> BASH
mv scripts/market/*.gd features/market/
mv scenes/market/*.tscn scenes/features/
任务 3.3: 其他模块
- 信箱模块 →
features/inbox/ - 关系模块 →
features/relationship/ - 势力模块 →
features/power/
阶段四:场景脚本重组(2 天)
任务 4.1: 主流程场景
</> BASH
# 移动主场景
mv scenes/Main.tscn scenes/main/
mv scenes/Login.tscn scenes/main/
mv scenes/RoleSelect.tscn scenes/main/
mv scenes/Hub.tscn scenes/main/
mv scenes/Game.tscn scenes/main/
mv scenes/Settlement.tscn scenes/main/
# 移动对应脚本
mv scripts/Main.gd scripts/main/
mv scripts/Login.gd scripts/main/
mv scripts/RoleSelect.gd scripts/main/
# ... 其他脚本
任务 4.2: UI 整理
</> BASH
# 清理冗余 ui/ 目录
mv ui/TimePanel.* ui/common/
# 移动弹窗组件
mv scenes/PublishRumorPanel.tscn ui/dialogs/
mv scripts/ui/PublishRumorPanel.gd ui/dialogs/
阶段五:路径更新与测试(2 天)
任务 5.1: 全局路径更新
使用文本编辑器批量替换:
</> PLAINTEXT
res://autoload/ → res://core/autoload/
res://src/ → res://core/systems/
res://scripts/autoload/→ res://core/autoload/
res://scripts/ui/ → res://scripts/features/
res://scenes/ui/ → res://scenes/features/
任务 5.2: 功能测试清单
- 登录流程
- 角色选择
- 主场景 Hub
- 听壁脚系统
- 情报系统
- 市场系统
- 信箱系统
- 关系系统
- 精力系统
- 数据库连接
阶段六:清理与优化(1 天)
任务 6.1: 删除旧目录
</> BASH
# 确认所有功能正常后
rm -rf autoload/
rm -rf src/
rm -rf scripts/autoload/
rm -rf scripts/data/
rm -rf scripts/market/
rm -rf scripts/inbox/
rm -rf scripts/ui/
rm -rf ui/
rm -rf scenes/market/
rm -rf scenes/ui/
任务 6.2: 更新文档
- 更新 README.md
- 创建架构文档
docs/architecture.md - 创建模块规范
docs/module-specifications.md
任务 6.3: 最终提交
</> BASH
git add .
git commit -m "refactor: complete directory structure reorganization
- Moved core systems to core/ directory
- Organized features into domain-based modules
- Unified naming conventions (PascalCase)
- Separated scenes and scripts by feature
- Added documentation
BREAKING CHANGE: All resource paths have changed.
See docs/architecture.md for new paths."
五、风险与缓解
风险 1: 路径引用错误
影响: 游戏无法启动,场景加载失败 缓解:
- 使用 Godot 编辑器的「重构」功能自动更新路径
- 分阶段提交,每阶段可回滚
- 完整测试清单
风险 2: Autoload 加载顺序
影响: 依赖关系错误 缓解:
- 保持原有 autoload 注册顺序
- 在
project.godot中明确标注依赖关系 - 测试启动流程
风险 3: 循环依赖
影响: 运行时错误 缓解:
- 使用 EventBus 解耦
- 核心层 → 功能层 单向依赖
- 代码审查
风险 4: 团队协作中断
影响: 其他成员代码冲突 缓解:
- 选择低峰期执行(如周末)
- 提前通知团队
- 使用独立分支
六、重构后对比
6.1 目录清晰度
| 指标 | 重构前 | 重构后 |
|---|---|---|
| 一级目录数 | 12 个 | 10 个 |
| UI 相关文件位置 | 3 处 | 2 处 |
| Autoload 位置 | 2 处 | 1 处 |
| 场景/脚本配对 | 困难 | 清晰 |
6.2 代码可维护性
| 指标 | 重构前 | 重构后 |
|---|---|---|
| 文件查找步骤 | 3-5 步 | 1-2 步 |
| 命名一致性 | 60% | 100% |
| 模块边界 | 模糊 | 清晰 |
| 新增模块成本 | 高 | 低 |
6.3 技术债务
| 问题 | 状态 |
|---|---|
| 目录分散 | ✅ 解决 |
| 命名混乱 | ✅ 解决 |
| 职责不清 | ✅ 解决 |
| 缺乏文档 | ✅ 解决 |
七、后续建议
7.1 代码质量提升
- 类型注解: 为所有 GDScript 添加类型提示
</> GDSCRIPT
# 改进前
func update_deficit(delta):
deficit_value = clamp(deficit_value + delta, 0.0, 100.0)
# 改进后
func update_deficit(delta: float) -> void:
deficit_value = clampf(deficit_value + delta, 0.0, 100.0)
- 单元测试: 为核心模块添加测试
</> GDSCRIPT
# tests/unit/test_game_state.gd
func test_deficit_update():
GameState.update_deficit(10.0)
assert(GameState.deficit_value == 10.0)
- 文档字符串: 为公共 API 添加文档
</> GDSCRIPT
## 开始挂机监听
## @param scene_key 场景标识
## @param duration_hours 时长(小时)
## @param partner_uid 双人搭档 ID(可选)
## @return 是否成功
func start_eavesdrop(scene_key: String, duration_hours: int, partner_uid: String = "") -> bool:
pass
7.2 架构演进
- 依赖注入: 减少 Autoload 全局依赖
- 事件总线: 强化 EventBus 使用
- 数据驱动: 将配置移至外部 JSON
7.3 工具链
- CI/CD: 自动运行测试
- 代码检查: GDLint 集成
- 性能分析: 定期性能测试
八、总结
本次重构将:
- ✅ 统一目录结构,减少认知负担
- ✅ 规范命名约定,提升可读性
- ✅ 明确模块边界,便于扩展
- ✅ 完善文档体系,降低维护成本
预计总耗时: 11 天(可分阶段执行) 风险等级: 中等(可通过测试缓解) 建议执行时机: 功能开发间隙或版本发布后
文档生成时间:2026-03-18 适用版本:Godot 4.6
评论