7.7 KiB
7.7 KiB
Unity 项目解耦架构重构 — 整体计划(v1.1)
配套详细诊断与改造映射见
Docs/解耦架构重构方案.md(已据同行评审修正至 v1.1) 当前状态:P0 地基 + P1 事件去静态化 + P2 大部分(去 Find/反射/Shader.Find)已落地,P2d 运行时集合接入、P3/P4/P5/P6 待推进
1. 目标
以 ScriptableObject 为通信总线,消除当前耦合腐烂信号:
- 7 个跨场景单例(
GameManager/AudioManager/ScoreManager/SceneLoader/TimeController/EnemyManager/LightMaskSystem) GameObject.Find/FindWithTag/FindObjectsByType散落运行时(含GameHUD条件性FindWithTag补救、EnemyAIStart 一次FindWithTag、MainMenuUIController对象名耦合)- UI 反射读私有字段(
GameHUD、EnemyHealthBar) - God Class(
GameManager/HealthSystem);PlayerController仅轻度耦合 static Action事件伪总线(onGameOver/onGameWin/onPlayerDamaged/OnEchoReleased/onScoreChanged)Resources.Load/Shader.Find字符串硬编码资源路径
结果形态:所有共享状态进 Variable 资产、跨系统消息走 Event 通道、实体集合走 RuntimeSet。MonoBehaviour 只认 SO 资产引用,彼此不再直连。
2. 目标架构分层
L0 SO 通信总线(项目级资产,跨场景存活,无 MonoBehaviour)
· Event 通道:GameEvent / IntEvent / Vector3Event / StringEvent / GameObjectEvent
· Variable 资产:FloatVariable / IntVariable / BoolVariable / Vector3Variable
· RuntimeSet<T>:玩家集 / 敌人集 / 灵灯集
↓ 监听/触发(Inspector 引用) ↓ 注册/读取
L1 系统服务(Audio/Scene/Time/Spawn) L2 实体(Player/Enemy/Lantern)
↓ 触发事件
L3 表现层(HUD/Overlay,只读 Variable + 监听 Event)
层间零直接类引用,谁触发/监听全在 Inspector 可见可配。
3. 四条铁律
- 调用方不碰单例 → 改
XxxEvent.Raise(),服务类自己订阅 - 生产代码零
GameObject.Find→ 找实体走RuntimeSet - 共享状态进 Variable → UI 订阅
OnValueChanged,不再反射/逐帧 Find - 一个 MonoBehaviour 一件事 → 拆分 God Class
4. 已落地地基(P0,增量零侵入)
Assets/Architecture/(未改动任何现有文件,可整体回滚):
Core/GameEvent.cs— 无参事件通道 +GameEventListenerCore/TypedGameEvents.cs—GameEvent<T>+ Int/Float/Vector3/String/GameObject 通道及监听器(含补全的StringEventListener)Variables/— Float/Int/Bool/Vector3 Variable,带OnValueChanged+ContextMenu重置RuntimeSets/RuntimeSet.cs—RuntimeSet<T>+TransformRuntimeSet+RuntimeSetRegistrar+ 泛型RuntimeSetRegistrar<T>Editor/VariableDrawer.cs— Inspector 实时显示变量值Variables/VariableRegistry.cs— 运行时状态泄漏防护:IVariable接口 +VariableRegistry,在每次进入 Play Mode 时([RuntimeInitializeOnLoadMethod(BeforeSceneLoad)])统一ResetToDefault(),防止 SO 变量跨局持久化(如 PlayerHealth 残留 0 直接触发死亡)Editor/AssetBootstrap.cs— 一键生成核心 SO 资产(菜单Architecture > Bootstrap Core Assets),已存在则跳过;变量同时写入_value与_defaultValue,从源头杜绝跨 Play Mode 泄漏
5. 分阶段路线图
| 阶段 | 目标 | 关键动作 | 风险 |
|---|---|---|---|
| P0 地基(已完成) | 引入通信总线 | 落地 Assets/Architecture/ + 创建首批资产 |
极低 |
| P1 事件去静态化 | 消灭 static Action 事件 |
onGameOver/onGameWin/onPlayerDamaged/onScoreChanged/onScoreSettled/OnEchoReleased → SO 事件资产,1:1 替换订阅 |
低 |
| P2 去掉 Find | 消灭 Find / 反射 | 建 PlayerRuntimeSet/EnemyRuntimeSet/LanternRuntimeSet,预制体挂 Registrar;改写 GameHUD/EnemyAI/GameManager/MainMenuUIController/GameLostOverlay/EnemyHealthBar |
中 |
| P3 去单例调用方 | 调用方不再 XxxManager.Instance |
Audio/Scene/Time/Score 改事件订阅者;ScorePickup/SoulDrop/ScoringUIController 改走 Variable/Event |
中 |
| P4 拆分 God Class | 单一职责 | GameManager→状态机+过场+敌人控制;HealthSystem→健康+死亡表现;PlayerController 轻量剥离 RequireComponent |
中高 |
| P5 资源与配置 | 去 Resources.Load/Shader.Find/魔法串 |
音频/视频→SerializeField 直接引用(AudioClip/VideoClip 拖到 SO/组件),不引入 Addressables(此项目体量下 Addressables 工程复杂度过高,需装包/建分组/异步加载);Shader.Find→资产引用;场景名/标签→SO 配置 |
中 |
| P6 工具与守门 | 防回归 | VariableDrawer(已完成)+ 构建期校验脚本(扫描生产代码 GameObject.Find)+ 设计师文档 |
低 |
建议节奏:P0→P1→P2 一个迭代内完成(收益最大、风险最低);P3/P4 按系统逐个推进;P5/P6 并行。
6. 关键待改文件清单(按阶段)
- P1:
GameManager.cs(去onGameOver/onGameWinstatic)、HealthSystem.cs、EchoSystem.cs、ScoreManager.cs(去onScoreChanged+onScoreSettled两个 static,二者需一并迁移) - P2:
GameHUD.cs(:104条件FindWithTag+:150-199反射)、EnemyHealthBar.cs(:50-53,73-75反射)、EnemyAI.cs(:112FindWithTag +:232Damage)、GameManager.cs(FindObjectsOfType<EnemyAI>)、MainMenuUIController.cs(:55,61,66,143对象名耦合)、GameLostOverlay.cs(:85自查找);并创建PlayerRuntimeSet/EnemyRuntimeSet/LanternRuntimeSet资产 - P3:
AudioManager/SceneLoader/TimeController/ScoreManager改订阅者;ScorePickup.cs:39/SoulDrop.cs:110/ScoringUIController.cs/MainMenuUIController.cs:206,216/GameLostOverlay.cs:246改Raise - P4:
GameManager.cs拆分;HealthSystem.cs拆分;Player.cs轻量剥离RequireComponent - P5:
AudioManager.cs:121/StoryPVPlayer.cs:141的Resources.Load→ SO 资产引用;Shader.Find共 5 处 → 序列化Shader字段引用:GameLostOverlay.cs:358(WaterRippleFade)、EchoSystem.cs:140(EchoRing,已有ringShader字段)、GroundBuilder.cs:180(AbyssEdgeGlow)、CliffWallBuilder.cs:108(AbyssEdgeGlow)、GroundClipTool.cs:19(SpriteWithGroundClip,Editor 脚本,运行时不进包,可保留 Shader.Find,但建议同样改字段引用));场景名魔法串 →SceneListSO - P6:构建期校验脚本(扫描
GameObject.Find)+ 设计师 SO 配置文档
7. 本迭代建议起点(P1+P2 验证链路)
- 运行菜单
Architecture > Bootstrap Core Assets一键生成首批资产(PlayerHealth/ScoreIntVariable、OnGameOver/OnGameWin/OnPlayerDied/OnPlayerDamagedGameEvent、EchoReleased/ScoreChanged/ScoreSettled带载荷事件、Players/EnemiesRuntimeSet);已存在则跳过 - 从最小系统切入:把
GameHUD接到PlayerHealth变量(消灭逐帧条件 Find 回退)+ 消除EnemyHealthBar反射(改用maxHealth/health的 IntVariable 订阅) - 跑通后按路线图逐阶段推进;具体系统改造指名文件即可
8. 风险与回滚
- 架构核心位于
Assets/Architecture/,可整体删除回滚;P1/P2 对既有文件的改造均为"加 SO 字段 + 改订阅方式",保留原逻辑,可逐文件回退 - 迁移保持"旧接口可用、新接口并行",每阶段独立验证,避免大爆炸重写
- 事件通道为引用语义:误删资产会在 Inspector 显示缺失引用(编译期可查),不会静默失效——比反射/单例安全