Add final evaluation report for Minimap system after all fixes and improvements
- Summarized the evolution of scores across five review rounds - Detailed the status of each evaluation dimension post-fixes - Highlighted remaining issues and recommended future work for further enhancements - Compared current system against industry benchmarks
This commit is contained in:
@@ -1,514 +0,0 @@
|
||||
# 开发实施计划
|
||||
|
||||
> **版本**:1.0
|
||||
> **日期**:2026-05-11
|
||||
> **依据**:`Docs/Architecture/`(24 份架构文档)
|
||||
> **目标**:完整实现 `Docs/Design/` 所有技术需求
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [策略选择](#1-策略选择)
|
||||
2. [整体阶段概览](#2-整体阶段概览)
|
||||
3. [Phase 0:项目基础](#3-phase-0项目基础)
|
||||
4. [Phase 1:垂直切片 MVP](#4-phase-1垂直切片-mvp)
|
||||
5. [Phase 2:核心玩法扩展](#5-phase-2核心玩法扩展)
|
||||
6. [Phase 3:世界与进程系统](#6-phase-3世界与进程系统)
|
||||
7. [Phase 4:内容与完善](#7-phase-4内容与完善)
|
||||
8. [模块依赖顺序](#8-模块依赖顺序)
|
||||
9. [技术风险与缓解策略](#9-技术风险与缓解策略)
|
||||
|
||||
---
|
||||
|
||||
## 1. 策略选择
|
||||
|
||||
### 为什么不用纯 MVP 先验证可行性?
|
||||
|
||||
纯 MVP 策略适合**架构未定型**的项目——先做最小功能,验证架构方向再继续。
|
||||
|
||||
本项目**架构已完全定型**(24 份文档,类/接口/字段/方法全部明确),逻辑设计无需验证。真正的风险集中在:
|
||||
|
||||
| 风险点 | 内容 |
|
||||
|--------|------|
|
||||
| 第三方库集成 | Animancer Pro FSM、PathBerserker2d NavSurface、Behavior Designer AI 树三者协作 |
|
||||
| SO 事件频道跨系统串联 | 首次接线需要验证整条链路(Input→Player→Combat→Save→UI) |
|
||||
| Addressables 异步加载 | 场景 Additive 加载 + Persistent 常驻场景的 Load/Unload 时序 |
|
||||
| Cinemachine + PixelPerfect | 相机切换与房间边界的实际表现 |
|
||||
|
||||
### 推荐:骨架垂直切片策略
|
||||
|
||||
```
|
||||
Phase 0: 项目骨架(无可玩内容,但基础设施就绪)
|
||||
↓
|
||||
Phase 1: 垂直切片 MVP(一个可玩房间,验证所有第三方集成点)
|
||||
↓
|
||||
Phase 2: 核心玩法扩展(在验证的骨架上填充完整战斗/移动系统)
|
||||
↓
|
||||
Phase 3: 世界与进程系统(地图、存档、谜题、任务)
|
||||
↓
|
||||
Phase 4: 内容与完善(Boss 技能、叙事、支撑系统、平台发布)
|
||||
```
|
||||
|
||||
每个 Phase 结束都有**可演示的里程碑**,但 Phase 0 之后的每一层都是在可运行的代码上追加,而非重写。
|
||||
|
||||
---
|
||||
|
||||
## 2. 整体阶段概览
|
||||
|
||||
| Phase | 目标 | 里程碑 | 预估周期 | 状态 |
|
||||
|-------|------|--------|---------|------|
|
||||
| **0** | 项目基础设施 | 编译无错,基础框架可寻址加载 | 1 周 | ✅ 完成(2026-05-07)|
|
||||
| **1** | 垂直切片 MVP | 一个房间可玩:移动/攻击/一只敌人/存读档 | 3–4 周 | ✅ 完成(2026-05-08)|
|
||||
| **2** | 核心玩法扩展 | 完整玩家能力树/战斗系统/多种敌人 | 4–5 周 | 🔄 代码全部完成(剩余:VFX 资产填充 - 仅 Unity 编辑器操作)|
|
||||
| **3** | 世界与进程 | 完整地图/谜题/任务/商店 | 4–5 周 | ✅ 代码全部完成(剩余:P3-1 Prefab 装配 - 仅 Unity 编辑器操作)|
|
||||
| **4** | 内容与完善 | Boss/叙事/平台服务/QA | 3–4 周 | ✅ 完成(2026-05-11,P4-1~P4-6 全部 ✅)|
|
||||
|
||||
---
|
||||
|
||||
## 3. Phase 0:项目基础
|
||||
|
||||
**目标**:所有模块能编译,Addressables/Assembly Definitions/SO 事件频道骨架就绪,无可玩内容。
|
||||
|
||||
### 任务清单
|
||||
|
||||
#### P0-1:项目结构(01_ProjectStructure)
|
||||
- [x] 按规范建立 `Assets/Scripts/`、`Assets/Data/`、`Assets/Prefabs/`、`Assets/Scenes/` 文件夹层级
|
||||
- [x] 创建全部 Assembly Definition 文件(`BaseGames.Core`、`BaseGames.Core.Events`、`BaseGames.Core.Save`、`BaseGames.Input`、`BaseGames.Player`、`BaseGames.Player.States`、`BaseGames.Combat`、`BaseGames.Combat.StatusEffects`、`BaseGames.Parry`、`BaseGames.Enemies`、`BaseGames.Enemies.AI`、`BaseGames.World`、`BaseGames.UI`、`BaseGames.Audio`、`BaseGames.Progression`、`BaseGames.Feedback`、`BaseGames.Camera`、`BaseGames.Animation`、`BaseGames.Editor`)
|
||||
- [x] 验证 asmdef 依赖方向(低层不引用高层,`Core.Events` 无任何依赖)
|
||||
- [x] 建立 `Persistent.unity` 场景骨架(空 GameObject 层级,待 Phase 1 填充组件)
|
||||
|
||||
#### P0-2:SO 事件系统(02_EventSystem)
|
||||
- [x] `BaseEventChannelSO<T>` 泛型基类
|
||||
- [x] `VoidBaseEventChannelSO` 无负载基类
|
||||
- [x] 所有具体频道类型(已创建):`VoidEventChannelSO`、`IntEventChannelSO`、`FloatEventChannelSO`、`BoolEventChannelSO`、`StringEventChannelSO`、`Vector2EventChannelSO`、`TransformEventChannelSO`、`GameStateEventChannelSO`、`SceneLoadRequestEventChannelSO`、`DifficultyChangedEventChannel`、`DamageInfoEventChannelSO`、`HitConfirmedEventChannelSO`、`ShopPurchaseEventChannelSO`、`DialogueEventChannelSO`、`LiquidEventChannelSO`、`WorldMarkerEventChannelSO`、`AbilityTypeEventChannelSO`(`AbilityType` payload)、`ColorBlindModeEventChannelSO`、`BossSkillEventChannelSO`、`BossPhaseEventChannelSO`、`StatusEffectEventChannelSO`、`QuestStateChangedEventChannel`、`QuestObjectiveEventChannelSO`
|
||||
- [x] `ToolUsedEventChannelSO`(payload:`ToolUsedPayload { SlotIndex, ToolId }`;`Core/Events/ToolEvents.cs`;⚠️ 架构 09 §7.5 用 `Tool` 对象,实现用 `string ToolId` 避免跨程序集依赖)
|
||||
- [x] `AchievementEventChannelSO`(payload:`AchievementSO`;`Progression/AchievementSO.cs`;Phase 0 骨架,Phase 4 扩充)
|
||||
- [x] 在 `Assets/Data/Events/` 下预建所有全局频道 `.asset` 资产(`CreateEventChannelAssets` Editor 菜单一键生成;对照 `02_EventSystem §4` 清单)
|
||||
- [x] `EventSubscription` struct + `CompositeDisposable`(已实现;命名差异:架构用 `EventSubscriptionToken`/`DisposableSubscription`,实现用 `EventSubscription`/`CompositeDisposable`——功能等价)
|
||||
- [x] `AddTo(ICollection<IDisposable>)` 扩展方法(`EventSubscriptionExtensions` 静态类;架构 02 §8)
|
||||
|
||||
#### P0-3:Core 骨架(03_CoreModule)
|
||||
- [x] `GameStateId` struct + `IGameState` 接口 + `GameStateMachine`(替代旧 `GameState` 枚举;架构 03_CoreModule §2)
|
||||
- [x] `GameStates` 静态工厂(8 个内置状态:MainMenu/Gameplay/Paused/BossFight/Cutscene/Loading/Dead/GameOver)
|
||||
- [x] `IGameStateFactory` 接口(供 DLC/扩展注册自定义状态)
|
||||
- [x] `GameStateEventChannelSO` payload 类型为 `GameStateId`(非旧 enum)
|
||||
- [x] `GameManager`(字段 + 接口签名,Awake 暂留空,内嵌 `GameStateMachine`)
|
||||
- [x] `SceneLoader`(异步加载骨架,`LoadSceneAsync` 封装)
|
||||
- [x] `SettingsManager` + `GlobalSettingsSO`
|
||||
- [x] `GlobalObjectPool`(通用池,`Spawn<T>` / `Despawn`;`WarmupAsync` 批量预热)
|
||||
- [x] `PooledObject` 组件
|
||||
|
||||
#### P0-3b:服务层骨架(03_CoreModule §11-13)
|
||||
- [x] `ServiceLocator` 静态类(`Register<T>` / `Get<T>` / `GetOrDefault<T>` / `OverrideForTest<T>` / `Reset`)
|
||||
- [x] `GameServiceRegistrar`(ExecutionOrder -2000,DontDestroyOnLoad;`Awake` 中注册所有核心服务)
|
||||
- [x] 五大服务接口:`IAudioService`、`ISaveService`、`ISceneService`、`IDeathRespawnService`、`IEventChannelRegistry`
|
||||
- [x] `NullAudioService`(空实现,测试/Editor 下兜底,不报错)
|
||||
- [x] `DeathRespawnService`(实现 `IDeathRespawnService`,UniTask-based,封装死亡/重生序列;Phase 1 完成完整逻辑,Day 3 只建文件+接口)
|
||||
- [x] `SceneService`(实现 `ISceneService`,UniTask-based,封装场景加载;Day 3 只建文件+接口)
|
||||
|
||||
#### P0-4:资产加载骨架(13_AssetPoolModule)
|
||||
- [x] `AddressKeys` 静态常量类(填入当前已知地址;后续持续追加)
|
||||
- [x] `AssetLoader` 工具类(封装 `Addressables.LoadAssetAsync`)
|
||||
- [x] `AssetReleaseTracker`(场景卸载时批量 Release)
|
||||
- [x] `AddressKeyRegistry`(运行时 key 注册层,DLC 扩展用;`TryRegister(key, addr)` / `Resolve(key)` / `TryResolve()` / `Unregister()` / `ForceRegister()`;`Core/Assets/AddressKeyRegistry.cs`;架构 13 §9)
|
||||
- [x] Editor 工具:`AddressKeyValidator`(反射扫描 `AddressKeys` 所有常量,对比 Addressable 分组)+ `AddressKeyImportWatcher`(`AssetPostprocessor`,Addressable 分组变更后自动触发;`Editor/AddressKeyValidator.cs`;架构 13 §10)
|
||||
|
||||
#### P0-5:存档骨架(12_SaveModule 部分)
|
||||
- [x] `SaveData` C# 完整数据结构(所有子类:`SaveMeta`、`PlayerSaveData`、`WorldSaveData` 等)
|
||||
- [x] `ISaveStorage` 接口 + `LocalFileStorage` 实现(⚠️ 类名无 "Save" 前缀,架构 12 §2)
|
||||
- [x] `SaveManager`(接口签名,`Save`/`Load`/`NewGame` 占位实现)
|
||||
|
||||
### Phase 0 完成标准
|
||||
- Unity 编译无错,Console 无警告
|
||||
- 能在 Editor 中运行 `Persistent` 场景(GameManager Awake 打印版本号)
|
||||
- 能新建 / 加载 JSON 存档文件(即使数据为空)
|
||||
- 所有 asmdef 引用方向通过 Assembly Reference Checker 验证
|
||||
|
||||
---
|
||||
|
||||
## 4. Phase 1:垂直切片 MVP
|
||||
|
||||
**目标**:一个完整可玩房间。验证所有第三方库集成点,建立可以往上叠加的可运行骨架。
|
||||
|
||||
**可玩标准**:玩家能在一个房间内移动、跳跃、普通攻击;有一只巡逻敌人能被击杀;房间内有存档点可存读档;死亡后从存档点复活。
|
||||
|
||||
### 任务清单
|
||||
|
||||
#### P1-1:输入系统(04_InputModule)
|
||||
- [x] `InputReaderSO`(持有 `PlayerInputActions` 引用,暴露 Unity Events)
|
||||
- [x] `InputBuffer`(三路独立缓冲:`_jumpBufferDuration = 0.15f`、`_attackBufferDuration = 0.12f`、`_dashBufferDuration = 0.10f`)
|
||||
- [x] 配置 `PlayerInputActions.inputactions`(Gameplay / UI / Cutscene 三套 Action Map;存于 `Assets/Settings/PlayerInputActions.inputactions`)
|
||||
- [x] 在 `Persistent` 场景挂载 `InputReader`
|
||||
|
||||
#### P1-2:相机系统(17_CameraModule)
|
||||
- [x] `CameraStateController`(管理 GlobalCam / RoomCam 优先级)
|
||||
- [x] `RoomVisibleArea`(每个房间场景的 `CinemachineConfiner2D` 边界)
|
||||
- [x] `CameraTriggerZone`(触发切换活跃相机)
|
||||
- [x] `CameraConfigSO`(Pixel Perfect 参数)
|
||||
- [x] `CameraBlendProfileSO`(混合曲线配置 SO,`ToBlendDefinition()` 返回 Cinemachine v3 结构体)
|
||||
- [x] `RoomCamera`(单房间 VCam 封装,`Activate()`/`Deactivate()` 优先级切换)
|
||||
- [x] 配置 Cinemachine Virtual Camera + Pixel Perfect Camera 组合(Unity Editor 场景组装)
|
||||
- [x] **验证点**:进入房间后相机锁定在正确边界,像素对齐无抖动
|
||||
|
||||
#### P1-3:玩家模块——基础(05_PlayerModule)
|
||||
目标:Run/Jump/Fall/Idle/Attack 五个状态可运行
|
||||
|
||||
- [x] `PlayerController`(协调器字段 + Awake 注册 + Update 状态转发)
|
||||
- [x] `PlayerMovement`(Rigidbody2D 移动,走/跳/落地)
|
||||
- [x] `PlayerStats`(HP 字段,`TakeDamage` 接口)
|
||||
- [x] `PlayerCombat`(基础近战攻击触发,HitBox 激活/停用)
|
||||
- [x] `PlayerStateBase`(抽象基类;`PlayerStateMachine` 逻辑内嵌于 `PlayerController`)
|
||||
- [x] State 实现:`IdleState`、`RunState`、`JumpState`、`FallState`、`AttackState`(✅)
|
||||
- [x] `PlayerAnimationConfigSO` + `PlayerMovementConfigSO`(基础数值)
|
||||
- [x] `PlayerStatsSO`(HP/灵力/弹簧/Geo 初始数值 SO)
|
||||
- [x] **Animancer 集成验证点**:FSM 切换时 Animancer Linear 混合正常,无动画卡顿
|
||||
|
||||
#### P1-4:战斗管道(06_CombatModule)
|
||||
- [x] `DamageInfo` struct + `Builder`
|
||||
- [x] 所有枚举(`DamageType`、`DamageCategory`、`DamageFlags`、`DamageTags`、`HitFxType`、`BreakLevel`、`PoiseLevel`;⚠️ `DamageTags` 为 `[Flags]` 枚举含 15 个标记位,`PoiseLevel` 均定义于架构 06_CombatModule §2)
|
||||
- [x] `DamageSourceSO`
|
||||
- [x] `HitBox`(`OnTriggerEnter2D` → 收集重叠的 `HurtBox`,调用 `ReceiveDamage`)
|
||||
- [x] `HurtBox`(`ReceiveDamage` 管道:防御计算 → `IDamageable.TakeDamage`)
|
||||
- [x] `IDamageable` 接口
|
||||
- [x] **伤害管道验证点**:玩家攻击命中敌人 HurtBox → 敌人 HP 减少,Console 打印 DamageInfo 内容
|
||||
|
||||
#### P1-5:基础敌人(07_EnemyModule)
|
||||
目标:一只能巡逻、追玩家、被攻击、死亡的基础敌人
|
||||
|
||||
- [x] `EnemyBase`(`IDamageable`,`TakeDamage`/`Die`)
|
||||
- [x] `EnemyStats` + `EnemyStatsSO`(基础 HP/攻击力)
|
||||
- [x] `EnemyMovement`(Rigidbody2D,配合 NavAgent)
|
||||
- [x] `EnemyNavAgent`(封装 PathBerserker2d `NavAgentComponent`)
|
||||
- [x] `EnemyCombat`(近战攻击区域,触发玩家 HurtBox)
|
||||
- [x] NavSurface 烘焙(测试房间地形)
|
||||
- [x] Behavior Designer 任务类:`BD_Patrol`、`BD_MoveToPlayer`、`BD_Attack`、`BD_IsPlayerInRange`(⚠️ 类名以架构 07_EnemyModule §8 为准,非 BD_ChasePlayer/BD_AttackPlayer/BD_CheckPlayerDistance)
|
||||
- [x] 一份基础 BehaviorTree 资产(Patrol → Detect → Chase → Attack)
|
||||
- [x] **PathBerserker2d + Behavior Designer 集成验证点**:敌人能在 NavSurface 上寻路追玩家
|
||||
|
||||
#### P1-6:存档完整实现(12_SaveModule)
|
||||
- [x] `SaveManager` 完整实现(`Save`、`Load`、`NewGame`、`DeleteSave`)
|
||||
- [x] `SavePoint` 场景组件(触发存档,广播 `_onSavePointActivated`)
|
||||
- [x] `ISaveable` 接口 + `PlayerSaveData` 读写(HP、位置、当前场景)
|
||||
- [x] `SaveMigrator`(版本号检查)
|
||||
- [x] `EmergencySaveService`(定时自动存档)
|
||||
- [x] **存档验证点**:Play → 激活存档点 → 退出 Play → 再 Play → 正确加载到存档位置
|
||||
|
||||
#### P1-7:最简 UI(10_UIModule)
|
||||
- [x] `UIManager`(Panel 层级管理,`OpenPanel`/`CloseTopPanel`;⚠️ 非 `ShowPanel`/`HidePanel`,架构 10 §2)
|
||||
- [x] `HUDController`(HP 条 + 当前货币;⚠️ 非 `HUDPanel`,架构 10 §3)
|
||||
- [x] 订阅 `_onHPChanged`、`_onGeoChanged` 事件频道更新 HUD(⚠️ 货币频道名为 `_onGeoChanged`,架构 10 §3)
|
||||
- [x] `DeathScreenController`(占位,显示"You Died";⚠️ 非 `DeathPanel`,架构 10 §6)
|
||||
|
||||
#### P1-8:最简音频钩子(11_AudioModule)
|
||||
- [x] `AudioManager`(单例,`PlaySFX`/`PlayBGM` 接口)
|
||||
- [x] `AudioMixerKeys`(Exposed Parameter 常量类)+ `AudioConfigSO`(区域 BGM 映射)
|
||||
- [x] `BGMController`(订阅 Zone/Boss 事件驱动 BGM 切换)+ `AudioZone`(区域切换触发)
|
||||
- [x] 在伤害管道和死亡事件上挂上 SFX 钩子(`CombatSFXController` 订阅 `HitConfirmedEventChannelSO` + `_onPlayerDied`)
|
||||
|
||||
#### P1-9:VFX 最简(18_VFXFeedbackModule 骨架)
|
||||
- [x] `VFXPool`(基于 `GlobalObjectPool` 的 VFX 专用包装)
|
||||
- [x] `HitFXSpawner`(根据 `DamageInfo.FxType` 路由到 VFX Prefab)
|
||||
- [x] `HurtFlashController`(白闪 Shader 参数控制)
|
||||
- [x] `VFXCatalogSO`(填入2-3个占位 Prefab)
|
||||
- [x] **验证点**:击中敌人时播放命中特效和白闪
|
||||
|
||||
#### P1-10:死亡复活流程串联(03_CoreModule §8)
|
||||
- [x] `GameManager` 完整实现死亡/复活时序
|
||||
- [x] 接通事件链:`_onPlayerDied` → `GameManager.OnPlayerDied()` → `DeathScreenController` → 等待输入 → `SceneLoader.ReloadFromSave()`
|
||||
|
||||
### Phase 1 完成标准
|
||||
| 验证项 | 要求 |
|
||||
|--------|------|
|
||||
| 玩家可移动/跳跃/攻击 | Animancer FSM 无卡顿 |
|
||||
| 敌人能寻路追击攻击 | PathBerserker2d + Behavior Designer 无报错 |
|
||||
| 攻击命中有 VFX 和音效 | 事件链完整 |
|
||||
| 激活存档点,退出重进,正确复位 | SaveManager JSON 读写正确 |
|
||||
| 玩家死亡 → 死亡画面 → 复活 | GameManager 状态机流转正确 |
|
||||
| 相机锁定房间,像素对齐 | 无亚像素抖动 |
|
||||
| Console 无错误,无 MissingReference | — |
|
||||
|
||||
---
|
||||
|
||||
## 5. Phase 2:核心玩法扩展
|
||||
|
||||
**目标**:在 Phase 1 的可运行骨架上,填充完整的玩家能力、战斗纵深、多种敌人。
|
||||
|
||||
### 任务清单
|
||||
|
||||
#### P2-1:玩家完整 FSM(05_PlayerModule 剩余)
|
||||
- [x] State 补充(Week 5):`DashState`、`AerialDashState`、`WallSlideState`、`WallJumpState`、`AirAttackState`、`DownAttackState`、`UpAttackState`、`HurtState`、`DeadState`、`SpringState`、`ParryState`
|
||||
- [x] `SwimState`(`21_LiquidPuzzleModule §SwimState`,✅ Week 11 完成)
|
||||
- [x] `PlayerWallDetector`(独立墙壁检测组件,双射线每侧防误判)
|
||||
- [x] `PlayerMovementConfigSO` 补充 `DefaultGravityScale`
|
||||
- [x] `FormController`(三形态切换:Sky/Earth/Death;含 `FormType`、`FormSO`、`FormConfigSO`)
|
||||
- [x] `WeaponManager`(FormController 联动 + Override 护符替换 API;含 `WeaponSO` 完整字段扩展)
|
||||
- [x] `PlayerCombat` 完整实现(HitBox 四向激活、`SetComboSegmentSource`、`OnHitConfirmed`→灵力)
|
||||
- [x] `PlayerController` Phase 2 扩展(`IPoiseSource` + 11 个新状态 + `TakeDamage` 路由到 HurtState/DeadState)
|
||||
- [x] `SkillManager`(技能槽 + 冷却计时)— 已在 P3-3 完整实现(`BaseGames.Skills` 程序集)
|
||||
- [x] `SpringSystem`(头部弹簧摆动,完整实现)
|
||||
- [x] Animancer 双层动画:Base Layer(移动动画)+ Overlay Layer(叠加层:灵泉/魂技能)✅ 2026-05-12(`PlayerController` 初始化 Layer 1;`PlayOnOverlay`/`StopOverlay` API;`SpringState` 改用叠加层)
|
||||
|
||||
#### P2-2:护盾系统(20_ShieldModule)
|
||||
- [x] `ShieldComponent`(Phase 2 完整实现:护盾值吸收、再生延迟、破盾惩罚事件;新增 `FullRecharge()` / `OnParrySuccess()` / 破碎惩罚计时;可选 `ShieldConfigSO` 覆盖内联字段)
|
||||
- [x] `ShieldConfigSO`(MaxShieldHP、DamageAbsorptionRatio、RechargeDelay、RechargeRate、BrokenPenaltyDuration、ParryRestoreRatio)
|
||||
- [x] `IShieldable` 接口(`CombatInterfaces.cs`)
|
||||
- [x] `HurtBox.ReceiveDamage` 接入护盾管道(步骤 4 `AbsorbDamage` 已实现,ConsumeParry 步骤 2 已实现)
|
||||
- [x] 护盾破碎/恢复 VFX 钩子 ✅ 2026-05-13(`ShieldComponent` 新增 `_onShieldBrokenChannel` / `_onShieldRestoredChannel` VoidEventChannelSO 字段,破碎时 Raise,破碎惩罚结束及 FullRecharge 时 Raise restore)
|
||||
|
||||
#### P2-3:完整战斗系统(06_CombatModule 剩余)
|
||||
- [x] `ProjectileConfigSO` + `LinearProjectile` + `ParryableProjectile`(+ `ArcProjectile`、`HomingProjectile`、`ProjectileManager`)
|
||||
- [x] `ParrySystem` 5 阶段状态机(Inactive/Startup/Active/EndLag/CounterWindow)+ 完美弹反子弹时间 + C# 事件 + `ParryConfigSO`(全部 12 个字段)
|
||||
- [x] `ParryInfo` struct + `ParryInfoEventChannelSO`(SO 事件频道,`BaseGames.Parry` 程序集)
|
||||
- [x] `PoiseWindowConfig` struct(`[Serializable]`,描述动画时间段霸体等级)+ `EnemyPoiseComponent`(`IPoiseSource` 实现,自动注入 HurtBox)
|
||||
- [x] `StatusEffectManager` + `StatusEffect`(抽象基类,架构 06_CombatModule §11,非 `StatusEffectBase`)+ 具体效果(Poison、Burn、Freeze、Stun)→ **已在 P2-4b 实现**
|
||||
- [x] `IBreakable`(机关/障碍物交互接口;`CombatInterfaces.cs` 内定义;`HitBox` 已在命中非 `HurtBox` 时尝试调用)
|
||||
- [x] `ClashResolver`(拼刀系统:同帧玩家/敌人 HitBox 对碰检测,广播 `EVT_NailClash`;架构 06 §15)✅ 2026-05-11(`ClashConfigSO.cs`、`ClashResolver.cs` 新建;`HitBox.cs` 加入拼刀检测分支,`CanClash` / `IsActive` / `OwnerRigidbody` 属性)
|
||||
|
||||
#### P2-4:动画事件系统(24_AnimEventModule)✅ Week 7(2026-05-11)
|
||||
- [x] `AnimationEventType` enum(21 种事件类型)
|
||||
- [x] `AnimationEventConfigSO`
|
||||
- [x] `AnimationEventBinder`
|
||||
- [x] `PlayerAnimationEvents`(挂在玩家上)
|
||||
- [x] `EnemyAnimationEvents`(挂在敌人上)
|
||||
- [x] `IAnimationEventHandler` 接口(⚠️ 非 `IAnimEventReceiver`,架构 24_AnimEventModule §1 patch)
|
||||
- [x] 将 AttackState HitBox 激活时机从 FixedUpdate 改为 AnimEvent 驱动(已通过 Animancer `events.Add(0.3f/0.6f, ...)` 归一化时间事件实现)
|
||||
- [x] Editor 工具:`EventConfigEditor`(`[CustomEditor(typeof(AnimationEventConfigSO))]`;时间轴标记点可视化、Clip 漂移检测(偏差>5帧警告)、normalizedTime 越界保护、事件类型着色;架构 24 §10)
|
||||
|
||||
#### P2-4b:状态效果系统(06_CombatModule §11)✅ Week 7(2026-05-11)
|
||||
- [x] `StatusEffectType.cs` 枚举(Fire/Poison/Stagger/Freeze/Stun;⚠️ 架构定义 4 个值 Fire/Poison/Freeze/Stun,Stagger 为实现扩展)
|
||||
- [x] `StatusEffect.cs` 抽象基类(双计时器:Duration + TickTimer;OnApply/OnStack/OnTick/OnExpire)
|
||||
- [x] `FireEffect.cs`(DoT,MaxStacks=1,**3s**/0.5s/**1dmg True**/tick;OnApply/OnExpire 设置 `_FireGlow` Shader 参数)
|
||||
- [x] `PoisonEffect.cs`(DoT,MaxStacks=3,**5s**/1s/**StackCount dmg True**/tick;OnApply/OnStack/OnExpire 管理 `_PoisonGlow` Shader 参数)
|
||||
- [x] `StaggerEffect.cs`(硬直,MaxStacks=1,可自定义持续时间;架构扩展,非核心规格)
|
||||
- [x] `StatusEffectEventChannelSO.cs`(SO 事件频道,携带 StatusEffectEvent struct;架构用 StatusEffectType 直接广播,实现扩展为含 StackCount/RemainingDuration 的 struct 更利 UI)
|
||||
- [x] `StatusEffectManager.cs` 完整实现(List + Dictionary 双结构;ApplyEffect/CleanseEffect/CleanseAll/HasEffect;`ApplyDirectDamage(DamageInfo)` 替代旧 DealDotDamage;`SetShaderParam(string,float)` via MaterialPropertyBlock;SpriteRenderer 初始化于 Awake)
|
||||
|
||||
#### P2-5:完整 VFX 反馈(18_VFXFeedbackModule 完整)
|
||||
- [x] `FeedbackConfigSO`(`HurtFlashColor`/`HurtFlashDuration`;Phase 2 扩充 Feel 封装字段)
|
||||
- [x] `IFeedbackPlayer` 接口(完整 API:PlayHit/PlayTakeHit/PlayDeath/PlayHeal/PlayLandImpact 等)+ `PlayerFeedback`、`EnemyFeedback`、`NullFeedbackPlayer` 实现
|
||||
- [ ] `VFXCatalogSO` 填入全部 VFX Prefab(命中/死亡/技能/环境)
|
||||
- [x] 接通 Feel MMF_Player 到相机震动、控制器震动 ✅ 2026-05-13(新增 `PostProcessManager`、`RegionLightController` + `RegionLightCatalogSO`、`PaletteSwapSystem` + `PaletteCatalogSO`;`BaseGames.VFX.asmdef` 加入 `BaseGames.Player` 引用)
|
||||
|
||||
#### P2-6:难度系统(19_DifficultyModule)✅ Week 8(2026-05-10)
|
||||
- [x] `DifficultyLevel` enum(已在 Core.Events,`Easy/Normal/Hard/SteelSoul`)
|
||||
- [x] `DifficultyScalerSO` × 4 份资产(Easy/Normal/Hard/SteelSoul — 脚本已创,Unity 内创建资产)
|
||||
- [x] `DifficultyManager`(`DefaultExecutionOrder: -900`,SteelSoul 不可降级)
|
||||
- [x] `BoolEventChannelSO`(新增,BossBase 依赖)
|
||||
- [x] `PlayerStats` 订阅 `DifficultyChangedEventChannel`,难度切换时按 HP 比例缩放(`hpRatio × newMax`)并广播 `_onHPChanged`/`_onMaxHPChanged` ✅ 2026-05-11
|
||||
- [x] `EnemyStats` 订阅 `DifficultyChangedEventChannel` 支持运行时切换;`Initialize()` 只缩放 HP,不缩放 Defense(架构 19 §5);提取 `ApplyHPScaler()` ✅ 2026-05-11
|
||||
- [x] `ShopController.GetEffectivePrice()` 新增公开方法;`TryPurchase()` 改用实际价格(含 `ShopPriceMultiplier`)✅ 2026-05-11
|
||||
- [x] `DeathRespawnService.StartGameOverCoroutine()` 实现:删除存档槽 → `ISceneService.LoadMainMenuCoroutine()` ✅ 2026-05-11
|
||||
- [x] `SaveManager.CurrentSlot` 公开属性(供 DeathRespawnService 获取当前槽)✅ 2026-05-11
|
||||
- [x] `GameManager.DeathFlow()` 接入 SteelSoul 分支:检查 `InstantDeathOnZeroHP`,true 时调用 `StartGameOverCoroutine()` 跳过普通死亡流程 ✅ 2026-05-11
|
||||
- [x] `DifficultyScalerSO` × 4 份资产(Easy/Normal/Hard/SteelSoul — 脚本已创,Unity 内创建资产)
|
||||
- [x] 钢铁之魂模式:`SaveMeta.IsSteelSoul` 字段写入 + `DifficultyManager` 实现 `ISaveable`(OnSave/OnLoad)在读档时锁定难度 ✅ 2026-05-11
|
||||
|
||||
#### P2-7:敌人扩展(07_EnemyModule 剩余)✅ Week 9(2026-05-10)
|
||||
- [x] `BossBase`(Phase 切换 EnterPhase/IsHPBelow/Die 广播)
|
||||
- [x] `ProjectileConfigSO` + `Projectile` (Linear/Arc/Homing/Parryable) + `ProjectileManager`
|
||||
- [x] `RangedEnemy` + `FlyingEnemy`(EnemyBase 子类)
|
||||
- [x] `DeathShade`(IInteractable,Geo 回收事件)
|
||||
- [x] `LootTableSO` + `LootResolver`(加权随机掉落 + 难度缩放;`SpawnGeo`/`SpawnItem` 调用 `CollectibleSpawner` 静态方法,Phase 3 TODO 占位已移除)✅ 2026-05-11
|
||||
- [x] `AttackPatternSO` + `TelegraphSystem` ✅ 2026-05-12
|
||||
- [x] `EnemyQuotaManager` ✅ 2026-05-12
|
||||
- [x] `BatchLOSSystem` + `ILOSRequester` ✅ 2026-05-12
|
||||
- [x] `BD_` 任务类补充(14 个 Action + Conditional)✅ 2026-05-12
|
||||
|
||||
#### P2-8:音频系统完整(11_AudioModule 完整)✅ Week 8(2026-05-10)
|
||||
- [x] `AudioEventSO`(随机音效 SO,Play / PlayOneShot)
|
||||
- [x] `GlobalSFXPlayer`(静态 SFX 入口,2D + 世界坐标 3D 播放)
|
||||
- [x] 为所有模块 SFX 钩子连接 `AudioEventSO` ✅ 2026-05-13(`CombatSFXController` 全部字段由 `AudioClip` 升级为 `AudioEventSO`,通过 `GlobalSFXPlayer.Play()` 播放,支持随机音量/音调/多片段)
|
||||
|
||||
---
|
||||
|
||||
## 6. Phase 3:世界与进程系统
|
||||
|
||||
**目标**:完整地图探索、谜题机关、任务系统、商店;存档数据完整;关卡编辑器工具就绪。
|
||||
|
||||
### 任务清单
|
||||
|
||||
#### P3-1:世界模块完整(08_WorldModule)✅ Week 10 代码完成
|
||||
- [x] `RoomTransition`(门/传送,Additive 场景加载/卸载)
|
||||
- [x] `HazardZone`(即死/持续伤害区域)
|
||||
- [x] `Collectible`(Geo/物品,弧线吸附)
|
||||
- [x] `DestructibleTile`(三帧图集,三种触发条件)+ `DirectionalDestructible`
|
||||
- [x] `DirectionalInteractable`(OneShot 持久化)
|
||||
- [x] `MovingPlatform`(三种 MoveType + 乘客 Parent 方案)
|
||||
- [x] `CrumblePlatform`(四态状态机 + NavLink 联动)
|
||||
- [x] `FalseWall`(三种揭示条件)
|
||||
- [x] `MagicWall`(仅 Gizmo,Layer Matrix 实现)
|
||||
- [x] `SoftTerrain`(Marker 组件)
|
||||
- [x] `PhantomInteractable`(继承 `DirectionalInteractable`,响应 `PhantomBody` 层)
|
||||
- [x] `WorldStateRegistry`(运行时缓存已触发/已破坏状态)
|
||||
- [x] `CollectibleSpawner`(静态生成器;`SpawnGeo(Vector2, int)` / `SpawnItem(Vector2, string)`;由 `LootResolver` 调用)+ `CollectibleSpawnerConfig`(MonoBehaviour,Persistent 场景持有 GeoPrefab/ItemPrefab 引用,Awake 注册到 CollectibleSpawner)✅ 2026-05-11
|
||||
- [x] `Collectible.SetGeo(int)` / `SetItem(string)`(运行时配置方法,由 CollectibleSpawner 实例化后调用;移除旧静态 SpawnGeo Debug.Log 占位)✅ 2026-05-11
|
||||
- [x] `RoomTransition.HasItem` 接入 `WorldStateRegistry.IsCollected(itemId)`(不再返回硬编码 true;新增 `[SerializeField] private WorldStateRegistry _worldState` 字段)✅ 2026-05-11
|
||||
- [ ] 场景内端对端验证(Unity 编辑器内 Prefab 装配 + 运行验证)
|
||||
|
||||
#### P3-2:液体与谜题系统(21_LiquidPuzzleModule)✅ Week 11 代码完成
|
||||
- [x] `LiquidZone` + `LiquidType` enum
|
||||
- [x] `LiquidPhysicsConfigSO`(浮力、水下速度)
|
||||
- [x] `SwimState`(已在 P3-2 完成)
|
||||
- [x] `ISwitchable`、`IActivatable` 接口
|
||||
- [x] `PuzzleSwitch`、`PuzzleReceiver`(AND/OR/XOR 逻辑)、`PuzzleWire`(可视化连接)
|
||||
- [x] `WaterDangerState` + `UnderwaterPostProcessingController`(架构 21 §12-13)
|
||||
- [x] `FootstepMaterial` + `FootstepAudioConfigSO` + `UnderwaterAudioController`(架构 21 §3.3-3.4)
|
||||
- [x] `WorldMarker` + `BreadcrumbTracker` ✅ 2026-05-12
|
||||
- [x] `TutorialManager` + `ContextualHintTrigger` ✅ 2026-05-12
|
||||
|
||||
#### P3-3:进度系统(09_ProgressionModule)✅ 完成(2026-05-10)
|
||||
- [x] `AbilityGate`(能力锁区域,检查 `PlayerStats.HasAbility()`)
|
||||
- [x] `AbilityUnlock`(解锁 Prefab,播放解锁演出)
|
||||
- [x] `CharmSO`、`EquipmentManager`(护符装备/卸载,Notch 管理)
|
||||
- [x] `CharmCatalogSO`(护符目录 SO,`Find(charmId)→CharmSO`;`EquipmentManager.AddToCollection` + `OnLoad` 依赖)✅ 2026-05-11
|
||||
- [x] `EquipmentManager.AddToCollection` 完整实现(从 CharmCatalogSO 查找 → 去重加入 `_collected`;旧 Debug.Log 占位已移除)✅ 2026-05-11
|
||||
- [x] `EquipmentManager.OnSave/OnLoad` 完整实现(读写 `OwnedCharmIds`;OnLoad 恢复 `_collected` 后逐个调用 `TryEquipCharm`)✅ 2026-05-11
|
||||
- [x] `FormSkillSO`、`SkillManager`、`SkillModifierRegistry`(技能数据+管理+修改器)
|
||||
- [x] `ToolSO`、`ToolSlotManager`(两槽工具 + 冷却)+ `ToolHUD`
|
||||
- [x] `ToolCatalogSO`(工具目录 SO,`Find(toolId)→ToolSO`;`ToolSlotManager.OnLoad` 依赖)✅ 2026-05-11
|
||||
- [x] `ToolSlotManager.OnLoad` 完整实现(从 ToolCatalogSO 查找两槽 toolId → 调用 `EquipTool(i, tool)`;旧 TODO 已移除)✅ 2026-05-11
|
||||
- [x] `EquipmentManager`(护符装备,多态 ICharmEffect)
|
||||
- [x] `RegionDefinitionSO`、`ProgressLock`、`BossProgressTracker`、`HPContainerPickup`
|
||||
- [x] `SkillHitBoxInstance`(技能 HitBox Prefab 组件)
|
||||
- [x] `CharmEffectDrawer`(Editor 护符效果下拉 Inspector)
|
||||
|
||||
#### P3-4:任务与挑战(22_QuestChallengeModule)✅ 完成(2026-05-11)
|
||||
- [x] `QuestSO`、`QuestObjectiveSO`(5 种具体类型)、`RewardSO`(物品发放通过 `StringEventChannelSO _onItemGranted.Raise(id)` 广播,旧 InventoryManager 占位已移除)✅ 2026-05-11
|
||||
- [x] `QuestManager`(ISaveable,事件驱动进度追踪)
|
||||
- [x] `QuestGiver`(继承 InteractableNPC,根据任务状态切换对话)
|
||||
- [x] `InteractableNPC`、`DialogueSequenceSO`(BaseGames.Dialogue 基础实现)
|
||||
- [x] `ChallengeRoomSO`、`ChallengeEncounterSO`、`BossRushSequenceSO`
|
||||
- [x] `ChallengeRoomManager`(Addressables 波次生成,快存/快读)
|
||||
- [x] `ChallengeRoomTrigger`(IInteractable 入口,事件触发场景加载)
|
||||
- [x] `EnemyBase` 扩展:`EnemyId` 属性 + `OnDied` 事件
|
||||
|
||||
#### P3-5:地图与商店(15_MapShopModule)✅ 完成(2026-05-11)
|
||||
- [x] `MapRoomDataSO` + `MapDatabaseSO` + `RoomExitData` + `ExitDirection`(房间数据 SO;架构 15 §1.1)
|
||||
- [x] `MapManager`(ISaveable;订阅 `EVT_RoomEntered`;`SetMapped(roomId)`;`[DefaultExecutionOrder(-700)]`;架构 15 §1.2)
|
||||
- [x] `MapPanel` + `MapRoomCellUI`(格子地图 UI;订阅 `EVT_MapUpdated` 增量刷新;架构 15 §1.3)
|
||||
- [x] `MapPlayerTracker`(`WorldToCell` 换算;LateUpdate 找所在房间;架构 15 §1.4)
|
||||
- [x] `MapPinManager`(ISaveable;`_pins: List<MapPin>`;MapPin/PinType 定义在 SaveData.cs 避免循环依赖;架构 15 §1.5)
|
||||
- [x] `ShopItemSO` + `ShopItemType`(架构 15 §2.1)
|
||||
- [x] `ShopInventorySO` + `RestockPolicy`(架构 15 §2.2)
|
||||
- [x] `ShopController`(ISaveable;`TryPurchase`;`GetAvailableItems`;`Restock`;`ShopPanel` 存根;架构 15 §2.3)
|
||||
- [x] `ShopNPC`(IInteractable;`DialogueEventChannelSO` 触发招呼对话→打开商店;架构 15 §2.4)
|
||||
- [x] Editor 工具:`MapRoomDataEditor`(`[CustomEditor(typeof(MapRoomDataSO))]`;Scene 句柄拖拽 GridPosition/GridSize;"居中 Scene View"按钮;架构 15 §5)
|
||||
|
||||
#### P3-6:存档完善(12_SaveModule 剩余)✅ 2026-05-11
|
||||
- [x] `CrashReporter` + `EmergencySaveService.PromoteToSlot` 完整实现(定时检点 + 崩溃日志)
|
||||
- [x] `SaveMigrator` 版本迁移策略(v1.0→v1.1→v2.0→v2.1 字段兼容)
|
||||
- [x] 存档槽 UI(`SaveSlotController` + `SaveSlotUI`;新建/删除/选择存档)
|
||||
- [x] `SaveManager.DeleteSlotAsync` + `BaseGames.UI.asmdef` 添加 `BaseGames.Core.Save` 引用
|
||||
|
||||
---
|
||||
|
||||
## 7. Phase 4:内容与完善
|
||||
|
||||
**目标**:Boss 战系统、叙事/过场、平台服务层、本地化、Debug 工具,进入 QA 可测试状态。
|
||||
|
||||
### 任务清单
|
||||
|
||||
#### P4-1:Boss 技能系统(23_BossSkillModule)✅ 2026-05-11
|
||||
- [x] `BossSkillTypes.cs`:全部枚举(`BossSkillCategory`、`BossSkillType`、`InteractionTag`、`VulnTriggerType`、`WeakPointType`、`CounterType`、`ArenaEventType`)及 Serializable struct(`VulnerabilityWindow`、`PlayerCounterResponse`、`ArenaEventTrigger`、`ArenaEventParams`、`ArenaEventData`、`BossResourceCost`、`IArenaInteractable`)
|
||||
- [x] `BossSkillSO`(`CreateAssetMenu`,引用 `AttackPatternSO[]`、`VulnerabilityWindow[]`、`PlayerCounterResponse[]`、`ArenaEventTrigger[]`、`BossResourceCost`、`PoiseWindowConfig`、`ClipTransition`)
|
||||
- [x] `AttackPatternSO`(伤害/弹幕/AoE/时序参数;`DamageSourceSO` 引用)
|
||||
- [x] `SkillSequenceSO`(`SequenceStep[]`,含重复逻辑 `RepeatIfPlayerInRange`)
|
||||
- [x] `BossResourceConfigSO`(Boss 自身资源配置,如愤怒值)
|
||||
- [x] `BossSkillExecutor`(Coroutine 实现;`ExecuteSkill()` / `InterruptCurrentSkill()`;VulnerabilityWindow 与攻击序列并行协程;注:架构规格为 UniTask,当前用 Coroutine 实现以匹配项目实际依赖)
|
||||
- [x] `WeakPointSystem`(弱点 HurtBox 激活管理;`SetActive(bool, float, bool)`;`_onVulnerabilityWindowOpened` 广播)
|
||||
|
||||
#### P4-2:叙事系统(14_NarrativeModule)✅ 2026-05-11
|
||||
- [x] `DialogueSequenceSO` 补全 `DialogueLine.portraitSprite` / `typewriterDelay` 字段 ✅ 2026-05-11
|
||||
- [x] `DialogueUI`(打字机效果,StringBuilder 零分配;`ShowLine` / `SkipTyping` / `Hide`;`IsTyping` 状态属性)✅ 2026-05-11
|
||||
- [x] `DialogueManager`(Coroutine 驱动打字序列;`StartDialogue(sequence, npcId)`;Action Map 切换;广播 `EVT_NpcDialogueCompleted`;注册到 ServiceLocator)✅ 2026-05-11
|
||||
- [x] `InteractionPromptController`(交互提示 UI;键盘/手柄图标自动切换;挂在 IInteractable 子节点)✅ 2026-05-11
|
||||
- [x] `NarrativeNPC`(扩展 InteractableNPC;`DialogueVersion[]` 条件对话版本;接入 `WorldStateRegistry`)✅ 2026-05-11
|
||||
- [x] `InteractableNPC.PlayDialogue()` 接入 `ServiceLocator.GetOrDefault<DialogueManager>()` ✅ 2026-05-11
|
||||
- [x] `EventChainSO` + 内置 7 个 `ChainCondition` + 10 个 `ChainAction`(全事件频道驱动,无跨程序集直接依赖;`BaseGames.EventChain` 新程序集)✅ 2026-05-11
|
||||
- [x] `EventChainManager`(订阅 5 条 StringEventChannelSO 中继;EvaluateAll;存档集成;防重入)✅ 2026-05-11
|
||||
- [x] `CutsceneSO`(`TimelineAsset`;`CutsceneBinding[]`;`CameraBlendProfileSO` 混合;`DialogueSequenceSO[]` 叠加层)✅ 2026-05-11
|
||||
- [x] `CutsceneManager`(`PlayableDirector` 封装;`PlayById` 事件驱动;Action Map 切换;`EVT_CutsceneStarted/Ended`)✅ 2026-05-11
|
||||
- [x] `CutsceneTrigger`(4 种模式:OnEnter/OnInteract/OnSceneLoad/OnEvent;实现 `IInteractable`;WorldStateRegistry 去重)✅ 2026-05-11
|
||||
- [x] `SignalEmitterClip` + `SignalEmitterBehaviour`(Timeline → VoidEventChannelSO 零耦合桥接;支持循环重播)✅ 2026-05-11
|
||||
|
||||
#### P4-3:输入重绑定(04_InputModule §6)✅ 2026-05-11
|
||||
- [x] `InputReaderSO` 补全重绑定 API:`StartRebinding()`(自动 Dispose op)、`SaveBindingOverrides()`、`LoadBindingOverrides()`、`ResetBindings()`、`GetAllActionMap()`、`FindAction()` ✅ 2026-05-11
|
||||
- [x] `InputReaderBootstrap` 启动时调用 `LoadBindingOverrides()`(在 `EnableGameplayInput()` 前)✅ 2026-05-11
|
||||
- [x] `ConflictDetector`(扫描 Gameplay Map 重复 effectivePath;返回冲突 Action 名称 HashSet)✅ 2026-05-11
|
||||
- [x] `RebindActionRow`(单行:显示当前绑定 HumanReadable 路径;排他锁 SetInteractable;冲突红色高亮)✅ 2026-05-11
|
||||
- [x] `RebindPanel`(统一协调排他重绑定流程;`OnResetAll`;完成后自动 `SaveBindingOverrides`)✅ 2026-05-11
|
||||
- [x] `BaseGames.UI.asmdef` 补充 `BaseGames.Input` + `Unity.InputSystem` 引用 ✅ 2026-05-11
|
||||
|
||||
#### P4-4:UI 完整(10_UIModule 剩余)
|
||||
- [x] `PauseMenuController`(设置/键位重绑定/退出;架构 10 §7.3)✅ 2026-05-11
|
||||
- [x] `SettingsPanelController`(音量/画质/帧率/VSync 设置面板;架构 10 §7.4)✅ 2026-05-11
|
||||
- [x] `BossHPBar`(Boss 血条 + 阶段标记;订阅 EVT_BossFightToggled/HPChanged;架构 10 §4)✅ 2026-05-11
|
||||
- [x] `SaveIndicator`(右下角存档提示;订阅 EVT_SaveIndicatorVisible;架构 10 §7.6)✅ 2026-05-11
|
||||
- [x] `LoadingOverlay`(全屏黑幕淡入淡出;订阅 EVT_LoadingOverlay;架构 10 §8)✅ 2026-05-11
|
||||
- [x] `LoadingScreenManager`(进度条 + 提示文字 + 随机背景;架构 10 §7.7)✅ 2026-05-11
|
||||
- [x] `FloatingDamageText` + `FloatingDamageSpawner`(对象池伤害飘字;订阅 EVT_DamageDealt;架构 10 §10)✅ 2026-05-11
|
||||
- [x] `ToastNotification` + `ToastManager`(成就/能力 Toast 队列弹出;架构 10 §11)✅ 2026-05-11
|
||||
- [x] `InputDeviceIconSetSO` + `InputDeviceIconSwitcher` + `InputIconImage`(键鼠/手柄图标切换;架构 10 §12)✅ 2026-05-11
|
||||
- [x] `BaseGames.UI.asmdef` 补充 `BaseGames.Combat` 引用 ✅ 2026-05-11
|
||||
- [x] `SaveSlotController` + `SaveSlotUI`(主菜单 3 槽存档卡片 UI;架构 10 §7.5)✅ 已在 P3-6 完成
|
||||
|
||||
#### P4-5:支撑模块(16_SupportingModules)✅ 已完成
|
||||
- [x] `LocalizationManager` + `LanguageManagerSO`(`#if UNITY_LOCALIZATION` 守卫;fallback 返回 key;架构 16 §5)
|
||||
- [x] `IPlatformService` ServiceLocator + `SteamPlatformService` + `NullPlatformService` + `PlatformBootstrap`(架构 16 §2)
|
||||
- [x] `AchievementManager`(通过 `ServiceLocator.Get<IPlatformService>()` 接入平台成就;12 条件类;架构 16 §3)
|
||||
- [x] `AnalyticsManager`(本地 JSON 日志;TrackBossKill/TrackDeath/TrackSessionStart/End;架构 16 §7)
|
||||
- [x] `DebugCheatSystem`(`#if UNITY_EDITOR || DEVELOPMENT_BUILD`;heal/addgeo/godmode/unlock/killall/scene/revive;架构 16 §4)
|
||||
- [x] `AntiSoftlockSystem` + `RoomEscapeInfoSO` + `HardAbilityGate`(卡关检测 + 自动解锁;架构 16 §6)
|
||||
- [x] `SpeedrunTimer`(unscaledDeltaTime;ISaveable;架构 16 §8)
|
||||
- [x] `AccessibilitySettingsSO` + `AccessibilityManager` + `ColorBlindFilter`(色盲矩阵 URP RenderFeature;架构 16 §6)
|
||||
- [x] `BaseGames.Support.asmdef`(引用 Core/Events/Save/Input/Player/Enemies/Combat/World/Progression/Platform/TMP/URP)
|
||||
|
||||
#### P4-6:编辑器工具(09_EditorExtensions)✅ 2026-05-11
|
||||
- [x] `AddressKeyValidator`(Build Pre-process hook;`AddressKeyValidatorBuildHook` 在 `AddressKeyValidator.cs` 补充;callbackOrder=0;架构 13 §10)✅ 2026-05-11
|
||||
- [x] `AddressReferenceGraphWindow`(`BaseGames/Tools/Asset Reference Graph`;扫描所有 `.cs` 对 `AddressKeys.X` 的引用,标红孤儿 key,橙色标注不在 Addressables 的 key,一键导出 CSV;架构 13 §11)✅ 2026-05-11
|
||||
- [x] `DirectionalDestructible`/`FalseWall`/`MovingPlatform`/`HazardZone`/`RoomTransition` Gizmo 已实现;`DestructibleTile` Gizmo(`DestructibleTileEditor.cs`)已补充;`PhantomPlate` 已创建 ✅ 2026-05-11
|
||||
- [x] `NavSurfaceBakeShortcut`(`%#b` 快捷键;`BaseGames/Tools/Bake All NavSurfaces`;完成时 SetDirty + 打印用时;架构 PathBerserker2d)✅ 2026-05-11
|
||||
- [x] `EventChannelEditor`(`VoidBaseEventChannelSO` RaiseInEditor 按钮;`BaseEventChannelSO<T>` 订阅者数量显示;Play Mode only;架构 02 §9)✅ 2026-05-11
|
||||
- [x] `CharmEffectDrawer`(`CharmSO.effects` 自定义 PropertyDrawer;实现为 `CharmSOEditor`,含类型下拉+效果预览;架构 16 §4.1)✅ 已在 P3-3 完成
|
||||
- [x] `SOValidationRunner` + `IValidatable` 接口(预构建 SO 数据验证;菜单 `Tools/Validate All SOs` + `IPreprocessBuildWithReport`;架构 16 §10)✅ 2026-05-11
|
||||
- [x] `EventBusMonitorWindow`(Editor Only;运行时事件监控面板,显示订阅者数量;架构 02 §9)
|
||||
- [x] `EventChainEditorWindow`(`BaseGames/Tools/Event Chain Viewer`;左侧链列表/右侧条件+动作表格;Play Mode 运行时着色绿/橙;`ChainCompletedCondition` 依赖链显示;执行日志最近 20 条;双击 PingObject;架构 14 §13)✅ 2026-05-11
|
||||
- [x] `BossSkillSequenceWindow`(`BaseGames/Tools/Boss Skill Sequence Viewer`;`BossSkillSO`/`SkillSequenceSO` 甘特图时间轴;Windup黄/Active红/Recovery灰/Vuln绿/Delay暗灰;拖放加载;架构 23 §12)✅ 2026-05-11
|
||||
- [x] `AchievementSOEditor`(`[CustomEditor(typeof(AchievementSO))]`;条件中文类型标签;内联展开 SO 字段;Ping/Delete 按钮;架构 16 §2.4)✅ 2026-05-11
|
||||
- [x] `BaseGames.Editor.asmdef` 补充 `BaseGames.EventChain` 引用 ✅ 2026-05-11
|
||||
|
||||
---
|
||||
|
||||
## 8. 模块依赖顺序
|
||||
|
||||
```
|
||||
BaseGames.Core.Events
|
||||
└─► BaseGames.Core(GameManager、SceneLoader、ObjectPool)
|
||||
└─► BaseGames.Core.Save(SaveManager)
|
||||
└─► BaseGames.Input(InputReaderSO)
|
||||
└─► BaseGames.Camera(CameraStateController)
|
||||
└─► BaseGames.Combat(DamageInfo、HitBox、HurtBox)
|
||||
└─► BaseGames.Player(PlayerController + FSM)
|
||||
└─► BaseGames.Enemies(EnemyBase + AI)
|
||||
└─► BaseGames.World(WorldInteractables、RoomTransition)
|
||||
└─► BaseGames.Progression(AbilityGate、Charms、Quest)
|
||||
└─► BaseGames.UI(全量 Panel)
|
||||
└─► BaseGames.Audio(AudioMixer + AudioEventSO)
|
||||
└─► BaseGames.Dialogue/Cutscene
|
||||
```
|
||||
|
||||
**原则**:低层模块不引用高层。`Core.Events` 是唯一无依赖模块,所有其他模块均可安全引用。
|
||||
|
||||
---
|
||||
|
||||
## 9. 技术风险与缓解策略
|
||||
|
||||
| 风险 | 严重度 | 缓解方案 |
|
||||
|------|--------|---------|
|
||||
| Animancer + Behavior Designer 冲突(双方均用 Update 驱动) | 🔴 高 | Phase 1 最先验证;`AnimancerComponent.Playable.Speed` 控制暂停,不与 BD 冲突 |
|
||||
| PathBerserker2d NavSurface 烘焙在移动平台上失效 | 🟠 中 | Phase 1 验证 `LocalNavSurface`;失效则退化到手动 NavLink |
|
||||
| Addressables 场景 Additive Load 内存泄漏 | 🟠 中 | `AssetReleaseTracker` 在 `SceneUnloaded` 回调中强制 Release;Phase 1 末验证 |
|
||||
| AudioEventSO 未完整填充导致 SFX 静音 | 🟡 低 | Phase 1 先用 `AudioManager.PlaySFX(AudioClip)` 直接传 Clip,Phase 2 完成 `AudioEventSO` 后统一替换引用 |
|
||||
| SO 事件频道大量 `.asset` 资产管理混乱 | 🟡 低 | `Assets/Data/Events/` 严格按模块子文件夹分组;命名规范 `EVT_{Module}_{Event}` |
|
||||
| 钢铁之魂模式存档不可降级误操作 | 🟡 低 | `SaveData.Meta.IsSteelSoul` 写入后 `DifficultyManager` 锁死选项,UI 二次确认 |
|
||||
@@ -1,753 +0,0 @@
|
||||
# Phase 0 · 项目基础设施
|
||||
|
||||
> **周期**:1 周
|
||||
> **前置条件**:Unity 2022.3 LTS 项目已创建,以下 Package 已导入:Cinemachine 3、New Input System、2D Pixel Perfect、Addressables、Newtonsoft Json、Kybernetik Animancer Pro、PathBerserker2d、Behavior Designer、Feel(More Mountains)
|
||||
> **产出物**:编译无错;能运行 Persistent 场景;能读写 JSON 存档;asmdef 依赖方向验证通过
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [实施顺序总览](#1-实施顺序总览)
|
||||
2. [Day 1:文件夹与 asmdef 骨架](#2-day-1文件夹与-asmdef-骨架)
|
||||
3. [Day 2:SO 事件系统](#3-day-2so-事件系统)
|
||||
4. [Day 3:Core 模块](#4-day-3core-模块)
|
||||
5. [Day 4:Addressables 与对象池骨架](#5-day-4addressables-与对象池骨架)
|
||||
6. [Day 5:SaveData 骨架 + Persistent 场景验证](#6-day-5savedata-骨架--persistent-场景验证)
|
||||
7. [完成标准检查清单](#7-完成标准检查清单)
|
||||
|
||||
---
|
||||
|
||||
## 1. 实施顺序总览
|
||||
|
||||
```
|
||||
Day 1: 文件夹结构 → asmdef 文件 → 依赖验证
|
||||
↓
|
||||
Day 2: BaseEventChannelSO 基类 → 所有具体频道类型 → Data/Events/ 资产
|
||||
↓
|
||||
Day 3: GameState + GameManager 骨架 → SceneLoader → SettingsManager + GlobalSettingsSO
|
||||
↓
|
||||
Day 4: AddressKeys → AssetLoader → GlobalObjectPool + PooledObject
|
||||
↓
|
||||
Day 5: SaveData C# 结构 → SaveManager 骨架 → Persistent 场景组装 → 全链路验证
|
||||
```
|
||||
|
||||
**关键原则**:每步完成后确认编译无错再继续。asmdef 创建顺序遵循依赖图(无依赖的先创建)。
|
||||
|
||||
---
|
||||
|
||||
## 2. Day 1:文件夹与 asmdef 骨架
|
||||
|
||||
### 2.1 创建文件夹结构
|
||||
|
||||
按 `01_ProjectStructure §1` 在 `Assets/` 下创建以下文件夹(Unity 中不存在同名 .meta 则空文件夹不提交,用 `.gitkeep` 占位):
|
||||
|
||||
```
|
||||
Assets/
|
||||
├── Scripts/
|
||||
│ ├── Core/
|
||||
│ │ ├── Events/
|
||||
│ │ └── Save/
|
||||
│ ├── Input/
|
||||
│ ├── Camera/
|
||||
│ ├── Player/
|
||||
│ │ └── States/
|
||||
│ ├── Combat/
|
||||
│ │ └── StatusEffects/
|
||||
│ ├── Parry/
|
||||
│ ├── Enemies/
|
||||
│ │ ├── AI/
|
||||
│ │ ├── Boss/
|
||||
│ │ │ └── Patterns/
|
||||
│ │ └── Navigation/
|
||||
│ ├── Feedback/
|
||||
│ ├── World/
|
||||
│ │ ├── Map/
|
||||
│ │ └── Shop/
|
||||
│ ├── UI/
|
||||
│ ├── Audio/
|
||||
│ ├── Progression/
|
||||
│ ├── Dialogue/
|
||||
│ ├── Equipment/
|
||||
│ ├── Cutscene/
|
||||
│ ├── Animation/
|
||||
│ ├── Spells/
|
||||
│ ├── Localization/
|
||||
│ ├── Tutorial/
|
||||
│ ├── Platform/
|
||||
│ └── Editor/
|
||||
│
|
||||
├── Data/
|
||||
│ ├── Events/
|
||||
│ │ ├── Core/
|
||||
│ │ ├── Player/
|
||||
│ │ ├── Combat/
|
||||
│ │ ├── World/
|
||||
│ │ └── UI/
|
||||
│ ├── Player/
|
||||
│ ├── Combat/
|
||||
│ ├── Enemies/
|
||||
│ ├── Progression/
|
||||
│ ├── Audio/
|
||||
│ ├── World/
|
||||
│ ├── UI/
|
||||
│ └── Settings/
|
||||
│
|
||||
├── Prefabs/
|
||||
│ ├── Player/
|
||||
│ ├── Enemies/
|
||||
│ ├── World/
|
||||
│ ├── UI/
|
||||
│ ├── Combat/
|
||||
│ ├── Effects/
|
||||
│ └── Persistent/
|
||||
│
|
||||
└── Scenes/
|
||||
```
|
||||
|
||||
### 2.2 创建 Assembly Definition 文件
|
||||
|
||||
**创建顺序(依赖图自底向上)**:
|
||||
|
||||
| 顺序 | 文件路径 | Assembly 名 | 引用 |
|
||||
|------|---------|-------------|------|
|
||||
| 1 | `Scripts/Core/Events/BaseGames.Core.Events.asmdef` | `BaseGames.Core.Events` | 无 |
|
||||
| 2 | `Scripts/Core/Save/BaseGames.Core.Save.asmdef` | `BaseGames.Core.Save` | Core.Events, Newtonsoft.Json |
|
||||
| 3 | `Scripts/Core/BaseGames.Core.asmdef` | `BaseGames.Core` | Core.Events, Core.Save |
|
||||
| 4 | `Scripts/Input/BaseGames.Input.asmdef` | `BaseGames.Input` | Core.Events, Unity.InputSystem |
|
||||
| 5 | `Scripts/Camera/BaseGames.Camera.asmdef` | `BaseGames.Camera` | Core.Events, Cinemachine |
|
||||
| 6 | `Scripts/Combat/BaseGames.Combat.asmdef` | `BaseGames.Combat` | Core.Events |
|
||||
| 7 | `Scripts/Combat/StatusEffects/BaseGames.Combat.StatusEffects.asmdef` | `BaseGames.Combat.StatusEffects` | Combat |
|
||||
| 8 | `Scripts/Parry/BaseGames.Parry.asmdef` | `BaseGames.Parry` | Combat |
|
||||
| 9 | `Scripts/Feedback/BaseGames.Feedback.asmdef` | `BaseGames.Feedback` | Core.Events, Combat |
|
||||
| 10 | `Scripts/Player/BaseGames.Player.asmdef` | `BaseGames.Player` | Core, Input, Combat, Parry, Feedback, Animancer |
|
||||
| 11 | `Scripts/Player/States/BaseGames.Player.States.asmdef` | `BaseGames.Player.States` | Player |
|
||||
| 12 | `Scripts/Enemies/BaseGames.Enemies.asmdef` | `BaseGames.Enemies` | Core, Combat, Feedback, Animancer |
|
||||
| 13 | `Scripts/Enemies/AI/BaseGames.Enemies.AI.asmdef` | `BaseGames.Enemies.AI` | Enemies, BehaviorDesigner.Runtime |
|
||||
| 14 | `Scripts/Enemies/Navigation/BaseGames.Enemies.Navigation.asmdef` | `BaseGames.Enemies.Navigation` | Enemies, PathBerserker2d |
|
||||
| 15 | `Scripts/World/BaseGames.World.asmdef` | `BaseGames.World` | Core, Combat, Animancer |
|
||||
| 16 | `Scripts/UI/BaseGames.UI.asmdef` | `BaseGames.UI` | Core.Events |
|
||||
| 17 | `Scripts/Audio/BaseGames.Audio.asmdef` | `BaseGames.Audio` | Core.Events |
|
||||
| 18 | `Scripts/Progression/BaseGames.Progression.asmdef` | `BaseGames.Progression` | Core, Player |
|
||||
| 19 | `Scripts/Equipment/BaseGames.Equipment.asmdef` | `BaseGames.Equipment` | Core.Events, Player |
|
||||
| 20 | `Scripts/Spells/BaseGames.Spells.asmdef` | `BaseGames.Spells` | Core.Events, Player, Combat |
|
||||
| 21 | `Scripts/World/Map/BaseGames.World.Map.asmdef` | `BaseGames.World.Map` | World, Core.Save |
|
||||
| 22 | `Scripts/World/Shop/BaseGames.World.Shop.asmdef` | `BaseGames.World.Shop` | World, Core.Events |
|
||||
| 23 | `Scripts/Dialogue/BaseGames.Dialogue.asmdef` | `BaseGames.Dialogue` | Core.Events |
|
||||
| 24 | `Scripts/Cutscene/BaseGames.Cutscene.asmdef` | `BaseGames.Cutscene` | Core.Events, Dialogue |
|
||||
| 25 | `Scripts/Animation/BaseGames.Animation.asmdef` | `BaseGames.Animation` | Core.Events, Animancer |
|
||||
| 26 | `Scripts/Enemies/Boss/Patterns/BaseGames.Enemies.Boss.Patterns.asmdef` | `BaseGames.Enemies.Boss.Patterns` | Enemies, Combat |
|
||||
| 27 | `Scripts/Localization/BaseGames.Localization.asmdef` | `BaseGames.Localization` | Core.Events |
|
||||
| 28 | `Scripts/Platform/BaseGames.Platform.asmdef` | `BaseGames.Platform` | Core.Events |
|
||||
| 29 | `Scripts/Tutorial/BaseGames.Tutorial.asmdef` | `BaseGames.Tutorial` | Core.Events, World |
|
||||
| 30 | `Scripts/Editor/BaseGames.Editor.asmdef` | `BaseGames.Editor` | 全部(Editor Only,`includePlatforms: ["Editor"]`) |
|
||||
|
||||
### 2.3 放置占位脚本
|
||||
|
||||
每个 asmdef 目录放一个 `_Placeholder.cs`(仅 namespace 声明),确保 Unity 不报"asmdef 无脚本"警告:
|
||||
|
||||
```csharp
|
||||
// _Placeholder.cs
|
||||
namespace BaseGames.Core { } // 各目录对应 namespace
|
||||
```
|
||||
|
||||
### 2.4 验证依赖方向
|
||||
|
||||
在 Unity Editor 打开 **Edit → Project Settings → Player → Other Settings** 确认无编译错误。
|
||||
确认底层 asmdef 的引用列表里**没有**高层 asmdef 名称。
|
||||
|
||||
---
|
||||
|
||||
## 3. Day 2:SO 事件系统
|
||||
|
||||
**参考文档**:`02_EventSystem.md`
|
||||
|
||||
### 3.1 实现基类(`Assets/Scripts/Core/Events/`)
|
||||
|
||||
按顺序创建以下文件:
|
||||
|
||||
```
|
||||
BaseEventChannelSO.cs ← 泛型基类 + VoidBaseEventChannelSO
|
||||
VoidEventChannelSO.cs ← [CreateAssetMenu]
|
||||
BoolEventChannelSO.cs
|
||||
IntEventChannelSO.cs
|
||||
FloatEventChannelSO.cs
|
||||
StringEventChannelSO.cs
|
||||
Vector2EventChannelSO.cs
|
||||
TransformEventChannelSO.cs
|
||||
GameState.cs ← 枚举(顺便在此处定义)
|
||||
GameStateEventChannelSO.cs
|
||||
SceneLoadRequest.cs ← struct(sceneName + entryId + loadingScreen)
|
||||
SceneLoadRequestEventChannelSO.cs
|
||||
DifficultyLevel.cs ← 枚举(Easy/Normal/Hard/SteelSoul)
|
||||
DifficultyChangedEventChannel.cs ← Phase 2 难度系统用(按架构命名,非 DifficultyEventChannelSO)
|
||||
DamageInfoEventChannelSO.cs ← Combat 伤害事件(EVT_DamageDealt)
|
||||
HitInfo.cs ← struct(DamageInfo + HitPoint Vector3)
|
||||
HitConfirmedEventChannelSO.cs ← VFX 命中事件(EVT_HitConfirmed,洛载类型 HitInfo)
|
||||
ShopPurchaseEvent.cs ← struct(⚠️ 架构 15_MapShopModule §2.3:ShopPurchaseEvent { Item, Price };非旧版 ShopTransactionEvent)
|
||||
ShopPurchaseEventChannelSO.cs ← 商店购买事件(EVT_ItemPurchased,⚠️ 架构 15 §2.3;非旧版 EVT_ShopTransactionCompleted)
|
||||
DialogueEventChannelSO.cs ← 对话请求事件(EVT_DialogueStartRequest,⚠️ payload 为 DialogueDataSO SO 引用,非 struct;无 DialogueRequest 类,架构 02 §3)
|
||||
LiquidEventChannelSO.cs ← 液体进出事件(EVT_LiquidEntered / EVT_LiquidExited)
|
||||
AbilityType.cs ← `[Flags] uint` 枚举(WallCling/WallJump/Dash/...) ⚠️ 位于 Scripts/Player/(程序集 BaseGames.Player),非 Scripts/Progression/(架构 09_ProgressionModule §1)
|
||||
AbilityTypeEventChannelSO.cs ← 能力解锁事件(仅内部保留;⚠️ EVT_AbilityUnlocked 实际使用 StringEventChannelSO,架构 02 §4)
|
||||
// ── Quest 事件频道(22_QuestChallengeModule §5)──────────────────────────────
|
||||
QuestState.cs ← 枚举(Unavailable/Available/Active/Completed/Failed)
|
||||
QuestStateChangedEvent.cs ← struct { string QuestId; QuestState State; }
|
||||
QuestStateChangedEventChannel.cs ← 任务状态变更(⚠️ 按架构命名,无 SO 后缀)
|
||||
QuestObjectiveEventChannelSO.cs ← 任务目标进度(payload: QuestObjectiveEvent{QuestId, ObjectiveId, Progress, Required},架构 02 §3)
|
||||
StatusEffectEventChannelSO.cs ← 状态效果施加/过期(payload: StatusEffectType 枚举,架构 02 §3)
|
||||
// ── Boss 技能事件频道(23_BossSkillModule §11)─────────────────────────────
|
||||
BossSkillEventChannelSO.cs ← Boss 技能开始/结束(payload: (string bossId, string skillId))
|
||||
BossPhaseEventChannelSO.cs ← Boss 阶段切换(payload: (string bossId, int phase))
|
||||
// ── 可访问性事件频道(16_SupportingModules §AccessibilityManager)──────────
|
||||
ColorblindMode.cs ← 枚举(None/Deuteranopia/Protanopia/Tritanopia)
|
||||
ColorblindModeEventChannelSO.cs ← 色觉模式切换事件(EVT_ColorblindModeChanged)
|
||||
// ── 导航标记事件频道(21_LiquidPuzzleModule §14)─────────────────────────────
|
||||
WorldMarkerEventChannelSO.cs ← 导航标记激活/失活(EVT_WorldMarkerActivated / EVT_WorldMarkerDeactivated)
|
||||
```
|
||||
|
||||
### 3.2 在 `Assets/Data/Events/` 下预建全局 SO 资产
|
||||
|
||||
命名规范:`EVT_{EventName}.asset`(不含模块前缀中的下划线,与架构 `02_EventSystem` 一致)
|
||||
|
||||
**Core 事件**(`Data/Events/Core/`):
|
||||
|
||||
| 资产名 | 类型 |
|
||||
|--------|------|
|
||||
| `EVT_PlayerDied` | `VoidEventChannelSO` |
|
||||
| `EVT_PlayerRespawned` | `VoidEventChannelSO` |
|
||||
| `EVT_PauseRequested` | `VoidEventChannelSO` |
|
||||
| `EVT_DeathScreenConfirmed` | `VoidEventChannelSO` |
|
||||
| `EVT_GameStateChanged` | `GameStateEventChannelSO` |
|
||||
| `EVT_SceneLoadRequest` | `SceneLoadRequestEventChannelSO` |
|
||||
| `EVT_SceneLoaded` | `StringEventChannelSO` |
|
||||
| `EVT_SavePointActivated` | `StringEventChannelSO` |
|
||||
| `EVT_DifficultyChanged` | `DifficultyChangedEventChannel` |
|
||||
|
||||
**UI 事件**(`Data/Events/UI/`):
|
||||
|
||||
| 资产名 | 类型 |
|
||||
|--------|------|
|
||||
| `EVT_FadeIn` | `VoidEventChannelSO` |
|
||||
| `EVT_FadeOut` | `VoidEventChannelSO` |
|
||||
| `EVT_FastTravelOpen` | `VoidEventChannelSO` |
|
||||
| `EVT_ShopOpened` | `StringEventChannelSO` |
|
||||
| `EVT_ShopClosed` | `VoidEventChannelSO` |
|
||||
| `EVT_MapOpen` | `VoidEventChannelSO` |
|
||||
| `EVT_ShowPanel` | `StringEventChannelSO`(panelId) |
|
||||
| `EVT_HidePanel` | `StringEventChannelSO`(panelId) |
|
||||
|
||||
**Player 事件**(`Data/Events/Player/`):
|
||||
|
||||
| 资产名 | 类型 |
|
||||
|--------|------|
|
||||
| `EVT_HPChanged` | `IntEventChannelSO` |
|
||||
| `EVT_MaxHPChanged` | `IntEventChannelSO` |
|
||||
| `EVT_SoulPowerChanged` | `IntEventChannelSO` |
|
||||
| `EVT_SpiritPowerChanged` | `IntEventChannelSO` |
|
||||
| `EVT_SpringChargesChanged` | `IntEventChannelSO` |
|
||||
| `EVT_GeoChanged` | `IntEventChannelSO` |
|
||||
| `EVT_PlayerFormChanged` | `IntEventChannelSO` |
|
||||
| `EVT_AbilityUnlocked` | `StringEventChannelSO`(abilityId,⚠️ 非 AbilityTypeEventChannelSO,架构 02 §4) |
|
||||
| `EVT_SkillSetChanged` | `VoidEventChannelSO`(发布: FormController,订阅: SkillHUD) |
|
||||
| `EVT_ShieldHPChanged` | `IntEventChannelSO`(发布: ShieldComponent,订阅: HUDController) |
|
||||
| `EVT_ShieldBroken` | `VoidEventChannelSO` |
|
||||
| `EVT_ShieldRestored` | `VoidEventChannelSO` |
|
||||
|
||||
**Combat 事件**(`Data/Events/Combat/`):
|
||||
|
||||
| 资产名 | 类型 |
|
||||
|--------|------|
|
||||
| `EVT_EnemyDied` | `TransformEventChannelSO` |
|
||||
| `EVT_DamageDealt` | `DamageInfoEventChannelSO` |
|
||||
| `EVT_HitConfirmed` | `HitConfirmedEventChannelSO` |
|
||||
| `EVT_ParrySuccess` | `VoidEventChannelSO` |
|
||||
| `EVT_BossFightStarted` | `StringEventChannelSO`(bossId;⚠️ 属战斗事件,架构 02 §4,非 Core 事件) |
|
||||
| `EVT_BossFightEnded` | `BoolEventChannelSO`(⚠️ 属战斗事件,架构 02 §4,非 Core 事件) |
|
||||
| `EVT_BossFightToggled` | `BoolEventChannelSO`(true=开始,false=结束) |
|
||||
| `EVT_BossHPChanged` | `IntEventChannelSO` |
|
||||
| `EVT_BossNameSet` | `StringEventChannelSO`(bossName) |
|
||||
| `EVT_BossHPMaxSet` | `IntEventChannelSO` |
|
||||
| `EVT_NailClash` | `VoidEventChannelSO` |
|
||||
| `EVT_StatusEffectApplied` | `StatusEffectEventChannelSO` |
|
||||
| `EVT_StatusEffectExpired` | `StatusEffectEventChannelSO` |
|
||||
| `EVT_BossSkillStarted` | `BossSkillEventChannelSO` |
|
||||
| `EVT_BossSkillEnded` | `BossSkillEventChannelSO` |
|
||||
| `EVT_BossVulnerabilityWindowOpened` | `StringEventChannelSO` |
|
||||
| `EVT_BossPhaseChanged` | `BossPhaseEventChannelSO` |
|
||||
|
||||
**World 事件**(`Data/Events/World/`):
|
||||
|
||||
| 资产名 | 类型 |
|
||||
|--------|------|
|
||||
| `EVT_CollectiblePickup` | `StringEventChannelSO` |
|
||||
| `EVT_RoomEntered` | `StringEventChannelSO` |
|
||||
| `EVT_RoomTransitionRequest` | `SceneLoadRequestEventChannelSO` |
|
||||
| `EVT_MapUpdated` | `StringEventChannelSO` |
|
||||
| `EVT_LiquidEntered` | `LiquidEventChannelSO` |
|
||||
| `EVT_LiquidExited` | `LiquidEventChannelSO` |
|
||||
| `EVT_DrownProgress` | `FloatEventChannelSO`(0–1 进度) |
|
||||
| `EVT_PlayerDrowned` | `VoidEventChannelSO` |
|
||||
| `EVT_GeoRecovered` | `StringEventChannelSO` |
|
||||
| `EVT_ShowInteractPrompt` | `StringEventChannelSO` |
|
||||
| `EVT_HideInteractPrompt` | `VoidEventChannelSO` |
|
||||
| `EVT_WorldMarkerActivated` | `WorldMarkerEventChannelSO` |
|
||||
| `EVT_WorldMarkerDeactivated` | `WorldMarkerEventChannelSO` |
|
||||
| `EVT_ItemPurchased` | `ShopPurchaseEventChannelSO` |
|
||||
|
||||
**Audio 事件**(`Data/Events/Audio/`):
|
||||
|
||||
| 资产名 | 类型 |
|
||||
|--------|------|
|
||||
| `EVT_PlayBGM` | `StringEventChannelSO` |
|
||||
| `EVT_StopBGM` | `VoidEventChannelSO` |
|
||||
| `EVT_PlaySFX` | `StringEventChannelSO` |
|
||||
| `EVT_RegionEntered` | `StringEventChannelSO` |
|
||||
|
||||
**Dialogue 事件**(`Data/Events/Dialogue/`):
|
||||
|
||||
| 资产名 | 类型 |
|
||||
|--------|------|
|
||||
| `EVT_DialogueStartRequest` | `DialogueEventChannelSO` |
|
||||
| `EVT_DialogueStarted` | `VoidEventChannelSO` |
|
||||
| `EVT_DialogueEnded` | `VoidEventChannelSO` |
|
||||
| `EVT_NpcDialogueCompleted` | `StringEventChannelSO` |
|
||||
| `EVT_CutsceneStarted` | `VoidEventChannelSO` |
|
||||
| `EVT_CutsceneEnded` | `VoidEventChannelSO` |
|
||||
|
||||
**Quest 事件**(`Data/Events/Quest/`):
|
||||
|
||||
| 资产名 | 类型 |
|
||||
|--------|------|
|
||||
| `EVT_QuestStarted` | `StringEventChannelSO`(questId,⚠️ 架构命名,对应 QuestManager.StartQuest(),架构 02 §4) |
|
||||
| `EVT_QuestCompleted` | `StringEventChannelSO`(questId) |
|
||||
| `EVT_QuestFailed` | `StringEventChannelSO`(questId) |
|
||||
| `EVT_ObjectiveUpdated` | `QuestObjectiveEventChannelSO`(QuestObjectiveEvent) |
|
||||
| `EVT_QuestStateChanged` | `QuestStateChangedEventChannel` |
|
||||
| `EVT_ChallengeCompleted` | `StringEventChannelSO` |
|
||||
| `EVT_ChallengeFailed` | `StringEventChannelSO` |
|
||||
|
||||
**事件链 / EventChain 事件**(`Data/Events/EventChain/`):
|
||||
|
||||
| 资产名 | 类型 |
|
||||
|--------|------|
|
||||
| `EVT_ChainCompleted` | `StringEventChannelSO` |
|
||||
| `EVT_DoorOpened` | `StringEventChannelSO` |
|
||||
| `EVT_FlagChanged` | `StringEventChannelSO` |
|
||||
|
||||
**Save 事件**(`Data/Events/Save/`):
|
||||
|
||||
| 资产名 | 类型 |
|
||||
|--------|------|
|
||||
| `EVT_SaveIndicatorVisible` | `BoolEventChannelSO` |
|
||||
|
||||
**Accessibility 事件**(`Data/Events/Accessibility/`):
|
||||
|
||||
| 资产名 | 类型 |
|
||||
|--------|------|
|
||||
| `EVT_AchievementUnlocked` | `StringEventChannelSO` |
|
||||
| `EVT_SoftlockDetected` | `VoidEventChannelSO` |
|
||||
| `EVT_ColorblindModeChanged` | `ColorblindModeEventChannelSO` |
|
||||
| `EVT_SubtitlesToggled` | `BoolEventChannelSO` |
|
||||
| `EVT_HighContrastToggled` | `BoolEventChannelSO` |
|
||||
|
||||
> **说明**:DamageInfoEventChannelSO、HitConfirmedEventChannelSO 等具体 channel 类型的脚本在 Day 2 §3.1 中一并创建;对应 SO 资产在创建完类型后立即在 Inspector 中创建。所有事件频道资产后续在各模块实现时如需补充,按此规范追加。
|
||||
|
||||
### 3.3 验证
|
||||
|
||||
在 Editor 中创建一个临时 `TestEventChannel.cs`,订阅 `EVT_PlayerDied.OnEventRaised` 并打印日志,在 Inspector 点击"Raise"按钮(需在 `BaseEventChannelSO` Editor 脚本里添加测试按钮)确认事件触发。
|
||||
|
||||
---
|
||||
|
||||
## 4. Day 3:Core 模块
|
||||
|
||||
**参考文档**:`03_CoreModule.md`
|
||||
|
||||
### 4.1 实现文件列表(`Assets/Scripts/Core/`)
|
||||
|
||||
```
|
||||
GameStateId.cs ← struct(替代旧 enum GameState,可扩展的状态 ID)
|
||||
IGameState.cs ← 状态接口(OnEnter / OnExit / Tick)
|
||||
GameStateMachine.cs ← 驱动所有 IGameState 切换的状态机
|
||||
GameStates.cs ← 静态工厂:提供 8 个内置状态实例(MainMenu/Gameplay/Paused/BossFight/Cutscene/Loading/Dead/GameOver)
|
||||
IGameStateFactory.cs ← DLC/扩展工厂接口(注册自定义状态)
|
||||
GameManager.cs ← 字段 + 接口签名(Awake/Start 骨架;内嵌 GameStateMachine)
|
||||
GameStateEventChannelSO.cs ← payload 改为 GameStateId(⚠️ 非旧枚举)
|
||||
SceneLoader.cs ← LoadAsync 骨架(监听 EVT_SceneLoadRequest)
|
||||
SettingsManager.cs ← Load/Save 设置
|
||||
GlobalSettingsSO.cs ← SO 数据类
|
||||
// ── 服务层骨架(03_CoreModule §11-13)───────────────────────────────────────
|
||||
ServiceLocator.cs ← 静态类(Register/Get/GetOrDefault/OverrideForTest/Reset)
|
||||
GameServiceRegistrar.cs ← MonoBehaviour,ExecutionOrder -2000,DontDestroyOnLoad
|
||||
IAudioService.cs ← 音频服务接口
|
||||
ISaveService.cs ← 存档服务接口
|
||||
ISceneService.cs ← 场景加载服务接口(UniTask-based)
|
||||
IDeathRespawnService.cs ← 死亡/重生服务接口(UniTask-based)
|
||||
IEventChannelRegistry.cs ← SO 事件频道查找接口
|
||||
NullAudioService.cs ← IAudioService 的空实现(测试兜底)
|
||||
DeathRespawnService.cs ← IDeathRespawnService 骨架(Phase 1 实现完整逻辑)
|
||||
SceneService.cs ← ISceneService 骨架(封装 SceneLoader,Phase 1 完整实现)
|
||||
```
|
||||
|
||||
> **⚠️ 架构升级**:`GameState` 枚举已全面替换为 `GameStateId` struct + `IGameState` 接口体系(架构 03_CoreModule §2)。
|
||||
> 旧写法 `TransitionTo(GameState.Gameplay)` 改为 `TransitionTo(GameStates.Gameplay)`;
|
||||
> 订阅方将收到 `GameStateId` 而非枚举值。
|
||||
|
||||
### 4.2 GameManager 实现优先级
|
||||
|
||||
Day 3 只实现**骨架部分**,不实现完整死亡/复活流程(Phase 1 完成):
|
||||
|
||||
```csharp
|
||||
// Day 3 实现范围
|
||||
// GameStateId 是 struct,不是 enum,通过 GameStates 静态类获取内置实例
|
||||
// IGameState 接口:void OnEnter(); void OnExit(); void Tick(float dt);
|
||||
// GameStateMachine 包含 TransitionTo(IGameState next),GameManager 内嵌实例
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// 1. 单例检查(DontDestroyOnLoad 由 Persistent 场景保证)
|
||||
// 2. 订阅事件频道(6个监听)
|
||||
// 3. _stateMachine.TransitionTo(GameStates.MainMenu) — 占位
|
||||
Debug.Log($"[GameManager] Awake v{Application.version}");
|
||||
}
|
||||
|
||||
// 占位实现(不报错即可)
|
||||
public void TransitionTo(IGameState newState) { _stateMachine.TransitionTo(newState); }
|
||||
public void Pause() { TransitionTo(GameStates.Paused); }
|
||||
public void Resume() { TransitionTo(GameStates.Gameplay); }
|
||||
```
|
||||
|
||||
### 4.3 SceneLoader 实现
|
||||
|
||||
```csharp
|
||||
// Day 3 实现范围
|
||||
// 订阅 EVT_SceneLoadRequest,执行 Addressables.LoadSceneAsync
|
||||
// 加载完成后发布 EVT_SceneLoaded
|
||||
// _currentRoomScene 记录当前场景名,用于 Unload
|
||||
```
|
||||
|
||||
### 4.4 GlobalSettingsSO
|
||||
|
||||
```csharp
|
||||
// ⚠️ 字段名、menuName、结构均必须与架构 03_CoreModule §7 一致
|
||||
[CreateAssetMenu(menuName = "Settings/GlobalSettings")]
|
||||
public class GlobalSettingsSO : ScriptableObject
|
||||
{
|
||||
[Header("Audio")]
|
||||
public float DefaultMasterVolume = 1f;
|
||||
public float DefaultBGMVolume = 0.8f;
|
||||
public float DefaultSFXVolume = 1f;
|
||||
public float DefaultAmbientVolume = 0.8f; // ⚠️ 与 AudioMixerKeys.Ambient 对应
|
||||
|
||||
[Header("Display")]
|
||||
public int DefaultTargetFPS = 60;
|
||||
public bool DefaultVSync = false;
|
||||
|
||||
[Header("Language")]
|
||||
public string DefaultLocaleCode = "zh-CN";
|
||||
|
||||
[Header("Accessibility")]
|
||||
public bool DefaultHighContrast = false;
|
||||
public bool DefaultScreenShake = true;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class GlobalSettingsData
|
||||
{
|
||||
public float MasterVolume;
|
||||
public float BGMVolume;
|
||||
public float SFXVolume;
|
||||
public float AmbientVolume; // ⚠️ 与 AudioMixerKeys.Ambient 对应
|
||||
public int TargetFPS;
|
||||
public bool VSync;
|
||||
public string LocaleCode;
|
||||
public bool HighContrast;
|
||||
public bool ScreenShake;
|
||||
}
|
||||
```
|
||||
|
||||
`SettingsManager.Awake()` 从 `PlayerPrefs` 恢复设置到 `GlobalSettingsData`;`Apply()` 方法将数据应用到 Unity `QualitySettings`/`Screen`/`PlayerPrefs`。
|
||||
|
||||
---
|
||||
|
||||
## 5. Day 4:Addressables 与对象池骨架
|
||||
|
||||
**参考文档**:`13_AssetPoolModule.md`
|
||||
|
||||
### 5.1 AddressKeys
|
||||
|
||||
```csharp
|
||||
// Assets/Scripts/Core/Assets/AddressKeys.cs
|
||||
// ⚠️ 命名规则:camelCase(无下划线分隔),字符串值保持原样(架构 13_AssetPoolModule §1 patch)
|
||||
public static class AddressKeys
|
||||
{
|
||||
// Scenes
|
||||
public const string ScenePersistent = "Scene_Persistent"; // ⚠️ 值为 "Scene_Persistent"(非 "Persistent")
|
||||
public const string SceneMainMenu = "Scene_MainMenu";
|
||||
public const string SceneTestRoom = "Scene_TestRoom"; // Phase 1 测试房间
|
||||
|
||||
// Player
|
||||
public const string PrefabPlayer = "PLY_Player"; // ⚠️ camelCase(非 Prefab_Player)
|
||||
|
||||
// 其余常量在各 Phase 按需追加(统一 camelCase 命名规则)
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 AssetLoader
|
||||
|
||||
```csharp
|
||||
// Assets/Scripts/Core/Assets/AssetLoader.cs
|
||||
// ⚠️ 架构使用标准 Task(非 UniTask),见 13_AssetPoolModule §5
|
||||
public static class AssetLoader
|
||||
{
|
||||
// 异步加载单个资产(带缓存)
|
||||
public static async Task<T> LoadAsync<T>(string addressKey) where T : UnityEngine.Object;
|
||||
|
||||
// 释放(减引用计数)
|
||||
public static void Release(string addressKey);
|
||||
|
||||
// 释放全部缓存
|
||||
public static void ReleaseAll();
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 GlobalObjectPool
|
||||
|
||||
```csharp
|
||||
// Assets/Scripts/Core/Pool/GlobalObjectPool.cs
|
||||
// ⚠️ 最终 API 见 13_AssetPoolModule §3;Phase 0 仅建骨架,不实现全量
|
||||
[DefaultExecutionOrder(-800)]
|
||||
public class GlobalObjectPool : MonoBehaviour
|
||||
{
|
||||
// 预热:按 _warmupConfigs 配置批量实例化所有条目(无参数,以 Task 返回)
|
||||
public async Task WarmupAsync();
|
||||
|
||||
// 获取对象(正式操作: Spawn)
|
||||
public T Spawn<T>(string addressKey, Vector3 position, Quaternion rotation) where T : Component;
|
||||
|
||||
// 归还对象(正式操作: Despawn)
|
||||
public void Despawn(string addressKey, GameObject instance);
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3.1 WarmupManifestSO(Phase 1 优化,架构 13 §12)
|
||||
|
||||
> Phase 0 仅建骨架;Phase 1 Vertical Slice 阶段完善 `SceneService.LoadSceneAsync()` 时一并实现。
|
||||
|
||||
```csharp
|
||||
// Assets/Scripts/Core/Pool/WarmupManifestSO.cs
|
||||
[CreateAssetMenu(menuName = "Core/Pool/Warmup Manifest")]
|
||||
public class WarmupManifestSO : ScriptableObject
|
||||
{
|
||||
[Serializable]
|
||||
public struct WarmupEntry
|
||||
{
|
||||
public string AddressKey; // AddressKeys 常量
|
||||
public int InitialCount; // 预热实例数
|
||||
public WarmupCategory Category;
|
||||
}
|
||||
|
||||
public enum WarmupCategory { Enemy = 0, Projectile = 1, VFX = 2, UI = 3, Other = 99 }
|
||||
|
||||
public WarmupEntry[] Entries;
|
||||
[Range(1, 20)] public int InstancesPerFrame = 5; // 每帧最多实例化数量(防卡顿)
|
||||
}
|
||||
|
||||
// GlobalObjectPool 新增方法(Phase 1 补充):
|
||||
// public async UniTask WarmupFromManifestAsync(WarmupManifestSO manifest, CancellationToken ct)
|
||||
// → 分帧预热:每帧最多实例化 manifest.InstancesPerFrame 个,await UniTask.Yield() 让出帧
|
||||
|
||||
// SceneService.LoadSceneAsync() 调用:
|
||||
// await GlobalObjectPool.Instance.WarmupFromManifestAsync(_warmupManifest, ct);
|
||||
|
||||
// 资产路径:Assets/Data/Pool/Warmup/Global_Warmup.asset、{SceneName}_Warmup.asset 等
|
||||
```
|
||||
|
||||
### 5.4 PooledObject
|
||||
|
||||
```csharp
|
||||
// Assets/Scripts/Core/Pool/PooledObject.cs
|
||||
// ⚠️ 完整实现(对齐架构 13_AssetPoolModule §4);Phase 0 先创建此完整版本,后续直接使用
|
||||
public class PooledObject : MonoBehaviour
|
||||
{
|
||||
public string AddressKey { get; private set; } // ⚠️ 属性而非公共字段(架构 13 §4)
|
||||
private GlobalObjectPool _pool;
|
||||
|
||||
// 由 GlobalObjectPool.SpawnInternal 调用,注入 key 和 pool 引用
|
||||
public void Setup(string key, GlobalObjectPool pool)
|
||||
{
|
||||
AddressKey = key;
|
||||
_pool = pool;
|
||||
}
|
||||
|
||||
// 子类可覆盖(从池中取出时调用)
|
||||
public virtual void OnSpawn() { }
|
||||
|
||||
// 子类可覆盖(归还到池时调用)
|
||||
public virtual void OnDespawn(){ }
|
||||
|
||||
// 便利方法:自归还
|
||||
public void ReturnToPool() => _pool?.Despawn(AddressKey, gameObject);
|
||||
|
||||
// 延迟归还(定时销毁型 VFX / 弹射物常用)
|
||||
public void ReturnToPoolDelayed(float delay) => StartCoroutine(DelayedReturn(delay));
|
||||
private IEnumerator DelayedReturn(float delay)
|
||||
{
|
||||
yield return new WaitForSeconds(delay);
|
||||
ReturnToPool();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.5 AssetReleaseTracker
|
||||
|
||||
```csharp
|
||||
// Assets/Scripts/Core/Assets/AssetReleaseTracker.cs
|
||||
// ⚠️ 完整实现对齐架构 13_AssetPoolModule §8:事件驱动,订阅 SceneLoadRequestEventChannelSO
|
||||
// ⚠️ 不使用 RegisterForScene/ReleaseScene 显式注册 API(SceneLoader 不主动调用本类)
|
||||
public class AssetReleaseTracker : MonoBehaviour
|
||||
{
|
||||
[Header("Event Channels")]
|
||||
[SerializeField] private SceneLoadRequestEventChannelSO _onSceneLoadRequest;
|
||||
|
||||
private string _lastLoadedScene;
|
||||
|
||||
private void OnEnable()
|
||||
=> _onSceneLoadRequest.OnEventRaised += OnSceneLoadRequested;
|
||||
private void OnDisable()
|
||||
=> _onSceneLoadRequest.OnEventRaised -= OnSceneLoadRequested;
|
||||
|
||||
private void OnSceneLoadRequested(SceneLoadRequest req)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_lastLoadedScene))
|
||||
{
|
||||
// 清除旧场景的对象池(⚠️ GlobalObjectPool.ClearPool 方法存在,架构 13_AssetPoolModule §3)
|
||||
GlobalObjectPool.Instance.ClearPool(AddressKeys.PrefabEnemyGrunt);
|
||||
// ... 其他场景对象(按需追加)
|
||||
}
|
||||
_lastLoadedScene = req.SceneName;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.6 Editor 验证工具
|
||||
|
||||
在 `Assets/Scripts/Editor/AddressKeysValidator.cs` 创建菜单项 `Tools/Validate AddressKeys`:
|
||||
- 反射读取 `AddressKeys` 所有 `const string`
|
||||
- 对照 `Addressables.ResourceLocators` 验证每个 key 是否存在
|
||||
- 不匹配的 key 输出 Warning
|
||||
|
||||
---
|
||||
|
||||
## 6. Day 5:SaveData 骨架 + Persistent 场景验证
|
||||
|
||||
**参考文档**:`12_SaveModule.md §1-4`
|
||||
|
||||
### 6.1 SaveData C# 数据结构
|
||||
|
||||
创建 `Assets/Scripts/Core/Save/` 下所有数据类(完整结构):
|
||||
|
||||
```
|
||||
SaveData.cs ← 顶层 + JsonExtensionData
|
||||
SaveMeta.cs ← 版本号、难度、游戏时间
|
||||
PlayerSaveData.cs ← HP、位置、灵力、形态等
|
||||
EquipmentSaveData.cs ← 护符相关(⚠️ 工具槽数据在独立的 ToolsSaveData,见架构 12_SaveModule §2)
|
||||
WorldSaveData.cs ← 房间状态、已破坏地形、已触发机关
|
||||
MapSaveData.cs ← 已探索房间列表
|
||||
QuestSaveData.cs ← 任务进度
|
||||
AchievementSaveData.cs
|
||||
ToolsSaveData.cs
|
||||
StatsSaveData.cs
|
||||
DeathShadeSaveData.cs
|
||||
ChallengeRoomsSaveData.cs ← 挑战房间进度(架构 12_SaveModule §1)
|
||||
EventChainsSaveData.cs ← 事件链状态(架构 12_SaveModule §1)
|
||||
ShopsSaveData.cs ← 商店已购记录(架构 12_SaveModule §1)
|
||||
NGPlusSaveData.cs ← New Game+ 数据(架构 12_SaveModule §1)
|
||||
```
|
||||
⚠️ `SaveData` 类**不含** `Tutorial` 字段(架构 12 §1 无 `TutorialSaveData`),教程进度应通过 `PlayerPrefs` 或独立 JSON 文件持久化,不经过存档系统。
|
||||
|
||||
### 6.2 ISaveStorage + LocalFileStorage
|
||||
|
||||
```csharp
|
||||
// Assets/Scripts/Core/Save/ISaveStorage.cs
|
||||
// ⚠️ 接口方法操作原始 JSON 字符串(架构 12_SaveModule §2);序列化/校验由 SaveManager 负责
|
||||
// ⚠️ 返回类型为标准 Task(非 UniTask)
|
||||
public interface ISaveStorage
|
||||
{
|
||||
Task WriteAsync(int slotIndex, string json);
|
||||
Task<string> ReadAsync(int slotIndex);
|
||||
Task DeleteAsync(int slotIndex);
|
||||
bool Exists(int slotIndex);
|
||||
IEnumerable<int> GetExistingSlots();
|
||||
}
|
||||
|
||||
// Assets/Scripts/Core/Save/LocalFileStorage.cs
|
||||
// ⚠️ 类名无 Save 前缀(架构 12_SaveModule §2)
|
||||
// 存档路径: Application.persistentDataPath/saves/save_{slot}.json
|
||||
public class LocalFileStorage : ISaveStorage { }
|
||||
```
|
||||
|
||||
### 6.3 SaveManager 骨架
|
||||
|
||||
```csharp
|
||||
// Assets/Scripts/Core/Save/SaveManager.cs
|
||||
// ⚠️ 返回类型为标准 Task(非 UniTask),见架构 12_SaveModule §4
|
||||
[DefaultExecutionOrder(-900)]
|
||||
public class SaveManager : MonoBehaviour
|
||||
{
|
||||
// 存档当前状态(由 EVT_SavePointActivated 触发)
|
||||
public async Task SaveAsync(int slot = -1); // 遍历 _saveables → 序列化 → WriteAsync
|
||||
// 读取并恢复存档(返回 false 表示槽位不存在或校验失败)
|
||||
public async Task<bool> LoadAsync(int slot); // ReadAsync → 反序列化 → 遍历 _saveables
|
||||
public bool SlotExists(int slot);
|
||||
public IEnumerable<int> GetExistingSlots();
|
||||
public void Register(ISaveable saveable);
|
||||
public void Unregister(ISaveable saveable);
|
||||
}
|
||||
```
|
||||
|
||||
### 6.4 组装 Persistent 场景
|
||||
|
||||
在 `Persistent.unity` 创建以下 GameObject 层级(组件留空或骨架绑定):
|
||||
|
||||
```
|
||||
[Managers]
|
||||
├── GameManager ← GameManager.cs,Inspector 绑定所有 EVT_ SO 资产
|
||||
├── SceneLoader ← SceneLoader.cs
|
||||
├── GlobalObjectPool ← GlobalObjectPool.cs
|
||||
├── SaveManager ← SaveManager.cs
|
||||
└── SettingsManager ← SettingsManager.cs + GlobalSettingsSO 资产引用
|
||||
```
|
||||
|
||||
### 6.5 全链路验证
|
||||
|
||||
创建 `Scenes/TestRoom_Phase0.unity`(空房间,一个平台的 Tilemap)并完成以下验证:
|
||||
|
||||
| 验证项 | 方法 |
|
||||
|--------|------|
|
||||
| Persistent 场景加载 + GameManager Awake 打印版本号 | Play `Persistent.unity`,Console 看 Log |
|
||||
| `SaveAsync(0)` 写入 JSON 存档文件(Phase 0 新游戏可手动构造初始 SaveData 后调用) | 菜单调用,检查 `persistentDataPath/saves/save_0.json` |
|
||||
| `LoadAsync(0)` 读取并填充 `CurrentSave` | Debug.Log 打印 `CurrentSave.Meta.PlayTime` |
|
||||
| `EVT_PlayerDied` Raise → `GameManager` 收到并打印 | Inspector "Raise" 按钮 |
|
||||
| `AddressKeys.SceneTestRoom` 能被 `SceneLoader` 加载 | 调用 `GameManager.LoadRoom` |
|
||||
|
||||
---
|
||||
|
||||
## 7. 完成标准检查清单
|
||||
|
||||
```
|
||||
✅ Unity 编译无错,Console 无 Error(脚本层全部创建完毕,2026-05-07)
|
||||
✅ 所有 asmdef 依赖方向正确(低层不引用高层)
|
||||
✅ SO 事件系统:Raise 能触发订阅者,OnDisable 能正确取消订阅
|
||||
□ GlobalSettingsSO 序列化/反序列化无 JSON 报错(待 Unity Editor 运行验证)
|
||||
□ SaveManager.SaveAsync(0) → 磁盘生成 save_0.json(待 Unity Editor 运行验证)
|
||||
□ SaveManager.LoadAsync → CurrentSave 非 null,数据匹配(待 Unity Editor 运行验证)
|
||||
□ SceneLoader 能 Additive 加载/卸载 TestRoom 场景(待 TestRoom 场景创建后验证)
|
||||
□ GlobalObjectPool.Spawn<PooledObject> 能返回实例,Despawn 能归还(待 Unity Editor 运行验证)
|
||||
□ AddressKeysValidator 无 Warning(当前定义的 key 均已在 Addressables 分组中)
|
||||
□ Persistent 场景 GameManager.Awake 打印版本号(待 Persistent 场景组装后验证)
|
||||
```
|
||||
|
||||
> **Phase 0 代码层完成于 2026-05-07。** 运行时验证项待 Phase 1 场景组装完成后一并执行。
|
||||
|
||||
**Phase 0 完成后进入 Phase 1。**
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user