chore: initial commit
This commit is contained in:
455
Docs/Plan/00_DevelopmentPlan.md
Normal file
455
Docs/Plan/00_DevelopmentPlan.md
Normal file
@@ -0,0 +1,455 @@
|
||||
# 开发实施计划
|
||||
|
||||
> **版本**:1.0
|
||||
> **日期**:2026-04-29
|
||||
> **依据**:`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 周 | — |
|
||||
| **3** | 世界与进程 | 完整地图/谜题/任务/商店 | 4–5 周 | — |
|
||||
| **4** | 内容与完善 | Boss/叙事/平台服务/QA | 3–4 周 | — |
|
||||
|
||||
---
|
||||
|
||||
## 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 剩余)
|
||||
- [ ] State 补充:`DashState`、`HurtState`、`DeadState`、`WallSlideState`、`WallJumpState`、`SwimState`(`21_LiquidPuzzleModule §SwimState`)
|
||||
- [ ] `FormController`(三形态切换:Normal/Soul/Spirit)
|
||||
- [ ] `WeaponManager`(`EquipAsync` 异步装备,Socket 管理)
|
||||
- [ ] `SkillManager`(技能槽 + 冷却计时)
|
||||
- [ ] `SpringSystem`(头部弹簧摆动)
|
||||
- [ ] Animancer 双层动画:Base Layer(移动动画)+ Additive Layer(攻击动画)
|
||||
|
||||
#### P2-2:护盾系统(20_ShieldModule)
|
||||
- [x] `ShieldComponent`(Phase 1 存根,AbsorbDamage 全量穿透;Phase 2 补完护盾值/再生/破盾事件)
|
||||
- [ ] `ShieldConfigSO`(MaxShieldHP、AbsorptionRatio、RechargeDelay、RechargeRate)
|
||||
- [x] `IShieldable` 接口(`CombatInterfaces.cs`)
|
||||
- [ ] `HurtBox.ReceiveDamage` 接入护盾管道(HurtBox → ShieldComponent → IDamageable)
|
||||
- [ ] 护盾破碎/恢复 VFX 钩子
|
||||
|
||||
#### P2-3:完整战斗系统(06_CombatModule 剩余)
|
||||
- [ ] `ProjectileConfigSO` + `LinearProjectile` + `ParryableProjectile`
|
||||
- [ ] `ParrySystem`(弹反窗口、弹反成功/失败事件)+ `ParryConfigSO`
|
||||
- [ ] `StatusEffectManager` + `StatusEffect`(抽象基类,架构 06_CombatModule §11,非 `StatusEffectBase`)+ 具体效果(Poison、Burn、Freeze、Stun)
|
||||
- [ ] `PoiseSystem`(霸体计数、Break 阈值)
|
||||
- [ ] `IBreakable`(机关/障碍物交互接口)
|
||||
- [ ] `ClashResolver`(拼刀系统:同帧玩家/敌人 HitBox 对碰检测,广播 `EVT_NailClash`;架构 06 §15)
|
||||
|
||||
#### P2-4:动画事件系统(24_AnimEventModule)
|
||||
- [ ] `AnimationEventType` enum(20+ 事件)
|
||||
- [ ] `AnimationEventConfigSO`
|
||||
- [ ] `AnimationEventBinder`
|
||||
- [ ] `PlayerAnimationEvents`(挂在玩家上)
|
||||
- [ ] `EnemyAnimationEvents`(挂在敌人上)
|
||||
- [ ] `IAnimationEventHandler` 接口(⚠️ 非 `IAnimEventReceiver`,架构 24_AnimEventModule §1 patch)
|
||||
- [ ] 将 AttackState HitBox 激活时机从 FixedUpdate 改为 AnimEvent 驱动
|
||||
- [ ] Editor 工具:`EventConfigEditor`(`[CustomEditor(typeof(AnimationEventConfigSO))]`;时间轴标记点可视化、Clip 漂移检测(偏差>5帧警告)、normalizedTime 越界保护、事件类型着色;架构 24 §10)
|
||||
|
||||
#### 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(命中/死亡/技能/环境)
|
||||
- [ ] 接通 Feel MMF_Player 到相机震动、控制器震动
|
||||
|
||||
#### P2-6:难度系统(19_DifficultyModule)
|
||||
- [ ] `DifficultyLevel` enum
|
||||
- [ ] `DifficultyScalerSO` × 4 份资产(Easy/Normal/Hard/SteelSoul)
|
||||
- [ ] `DifficultyManager`(`DefaultExecutionOrder: -900`)
|
||||
- [ ] `EVT_DifficultyChanged` 频道
|
||||
- [ ] `PlayerStats`/`EnemyStats`/`ShopController` 订阅难度系数
|
||||
- [ ] 钢铁之魂模式:一命锁定 + SaveData 标记
|
||||
|
||||
#### P2-7:敌人扩展(07_EnemyModule 剩余)
|
||||
- [ ] `BossBase` + `BossOrchestrator`(Phase 切换)
|
||||
- [ ] `AttackPatternSO` + `TelegraphSystem`(攻击前摇标注)
|
||||
- [ ] `DeathShade`(死亡遗骸 Prefab)
|
||||
- [ ] `LootTableSO`(战利品掉落配置 SO;`BaseWeight`、`ScaleWithDifficulty`;架构 07 §14)+ `LootResolver`(按概率掷骰)+ `LootPickup`(弧线飞行动画)
|
||||
- [ ] `EnemyQuotaManager`(同屏最多 12 个活跃 BT;每 10 帧重新评估优先级;架构 07 §13.5)
|
||||
- [ ] `BatchLOSSystem` + `ILOSRequester`(Jobs+Burst 批量视线检测;超过 4 个活跃敌人时必须注册;架构 07 §15)
|
||||
- [ ] 敌人 Prefab 模板:远程弓箭手(含 Projectile 发射)
|
||||
- [ ] `BD_` 任务类补充:`BD_FlyToTarget`、`BD_DropDown`、`BD_Teleport`、`BD_TelegraphAttack`
|
||||
|
||||
#### P2-8:音频系统完整(11_AudioModule 完整)
|
||||
- [ ] `AudioEventSO`(SFX 配置 SO,持有 `AudioClip` + `volume` + `pitch` 随机化范围)
|
||||
- [ ] `GlobalSFXPlayer`(全局静态 SFX 播放入口,配合对象池管理位置 SFX)
|
||||
- [ ] `AudioConfigSO` 完整填表(区域 BGM 映射 + 快照名称枚举)
|
||||
- [ ] 为所有模块 SFX 钩子连接 `AudioEventSO`(替代直接传递 `AudioClip`)
|
||||
|
||||
---
|
||||
|
||||
## 6. Phase 3:世界与进程系统
|
||||
|
||||
**目标**:完整地图探索、谜题机关、任务系统、商店;存档数据完整;关卡编辑器工具就绪。
|
||||
|
||||
### 任务清单
|
||||
|
||||
#### P3-1:世界模块完整(08_WorldModule)
|
||||
- [ ] `RoomTransition`(门/传送,Additive 场景加载/卸载)
|
||||
- [ ] `HazardZone`(即死/持续伤害区域)
|
||||
- [ ] `Collectible`(Geo/物品,弧线吸附)
|
||||
- [ ] `DestructibleTile`(三帧图集,三种触发条件)+ `DirectionalDestructible`
|
||||
- [ ] `DirectionalInteractable`(OneShot 持久化)
|
||||
- [ ] `MovingPlatform`(三种 MoveType + 乘客 Parent 方案)
|
||||
- [ ] `CrumblePlatform`(四态状态机 + NavLink 联动)
|
||||
- [ ] `FalseWall`(三种揭示条件)
|
||||
- [ ] `MagicWall`(仅 Gizmo,Layer Matrix 实现)
|
||||
- [ ] `SoftTerrain`(Marker 组件)
|
||||
- [ ] `PhantomInteractable`(继承 `DirectionalInteractable`,响应 `PhantomBody` 层)
|
||||
- [ ] `WorldStateRegistry`(运行时缓存已触发/已破坏状态)
|
||||
|
||||
#### P3-2:液体与谜题系统(21_LiquidPuzzleModule)
|
||||
- [ ] `LiquidZone` + `LiquidType` enum
|
||||
- [ ] `LiquidPhysicsConfigSO`(浮力、水下速度)
|
||||
- [ ] `SwimState`(已在 P2-1 添加到 FSM)
|
||||
- [ ] `ISwitchable`、`IActivatable` 接口
|
||||
- [ ] `PuzzleSwitch`、`PuzzleReceiver`(AND/OR/XOR 逻辑)、`PuzzleWire`(可视化连接)
|
||||
- [ ] `WorldMarker` + `BreadcrumbTracker`(导航提示)
|
||||
- [ ] `TutorialManager` + `ContextualHintTrigger`
|
||||
|
||||
#### P3-3:进度系统(09_ProgressionModule)
|
||||
- [ ] `AbilityGate`(能力锁区域,检查 `PlayerStats.unlockedAbilities`)
|
||||
- [ ] `AbilityUnlock`(解锁 Prefab,播放解锁演出)
|
||||
- [ ] `CharmSO`、`CharmManager`(护符装备/卸载,Slot 管理)
|
||||
- [ ] `SkillSO`、`SpellSO`(技能/魔法数据)
|
||||
- [ ] `ToolSlotManager`(两槽工具 + 冷却)+ `ToolHUD`
|
||||
- [ ] `EquipmentManager`(武器/防具装备)
|
||||
|
||||
#### P3-4:任务与挑战(22_QuestChallengeModule)
|
||||
- [ ] `QuestSO`、`QuestObjectiveSO`、`RewardSO`
|
||||
- [ ] `QuestManager`(进度追踪,订阅世界事件)
|
||||
- [ ] `QuestGiver`(`IInteractable` NPC)
|
||||
- [ ] `ChallengeRoomSO`、`ChallengeEncounterSO`、`BossRushSequenceSO`
|
||||
- [ ] `ChallengeRoomManager`
|
||||
|
||||
#### P3-5:地图与商店(15_MapShopModule)
|
||||
- [ ] `MapManager`(订阅 `EVT_RoomEntered` 事件,调用 `OnRoomEntered(string roomId)` 记录已探索房间;⚠️ 无 `RevealRoom` 公共方法,架构 15 §1.2 patch)
|
||||
- [ ] `MapDatabaseSO` + `MapRoomDataSO`(房间数据 SO;⚠️ 非 `RoomRevealData`,架构 15 §1.1)
|
||||
- [ ] `MapUI`(房间格子渲染,玩家位置实时标记,破坏/机关状态叠加)
|
||||
- [ ] `FastTravelSystem`(快速传送锚点)
|
||||
- [ ] `ShopController`(`TryPurchase`,难度价格倍率接入)
|
||||
- [ ] `ShopInventorySO`
|
||||
- [ ] Editor 工具:`MapRoomDataEditor`(`[CustomEditor(typeof(MapRoomDataSO))]`;Scene 句柄拖拽 GridPosition/GridSize,消除手动整数对齐误差;"居中 Scene View 到此房间"按钮;架构 15 §5,P3 优化)
|
||||
|
||||
#### P3-6:存档完善(12_SaveModule 剩余)
|
||||
- [ ] `CrashReporter` + `EmergencySaveService` 定时检点完整实现
|
||||
- [ ] `SaveMigrator` 版本迁移策略(v1→v2 字段兼容)
|
||||
- [ ] 存档槽 UI(新建/删除/选择存档)
|
||||
|
||||
---
|
||||
|
||||
## 7. Phase 4:内容与完善
|
||||
|
||||
**目标**:Boss 战系统、叙事/过场、平台服务层、本地化、Debug 工具,进入 QA 可测试状态。
|
||||
|
||||
### 任务清单
|
||||
|
||||
#### P4-1:Boss 技能系统(23_BossSkillModule)
|
||||
- [ ] `BossSkillSO`、`BossSkillType` enum
|
||||
- [ ] `AttackPatternSO`(已在 P2-7 基础上扩展)
|
||||
- [ ] `SkillSequenceSO`(多阶段技能编排)
|
||||
- [ ] `BossSkillExecutor`
|
||||
- [ ] `WeakPointSystem`(脆弱点组件,命中切换 Boss 阶段)
|
||||
- [ ] `VulnerabilityWindow`
|
||||
|
||||
#### P4-2:叙事系统(14_NarrativeModule)
|
||||
- [ ] `DialogueManager`(DialogueGraph SO 驱动)
|
||||
- [ ] `CutsceneManager` + `CutsceneSO` + `CutsceneTrigger`
|
||||
- [ ] `EventChainSO`(线性事件链触发器)
|
||||
- [ ] `InteractableNPC` 基类(`IInteractable`,触发对话)
|
||||
|
||||
#### P4-3:输入重绑定(04_InputModule §6)
|
||||
- [ ] `RebindPanel`
|
||||
- [ ] `RebindActionRow`
|
||||
- [ ] `ConflictDetector`
|
||||
- [ ] `RebindPersistence`(写入 `PlayerPrefs`)
|
||||
|
||||
#### P4-4:UI 完整(10_UIModule 剩余)
|
||||
- [ ] `PauseMenu`(设置/键位重绑定/退出)
|
||||
- [ ] `InventoryPanel`、`EquipmentPanel`、`MapPanel`、`SkillPanel`
|
||||
- [ ] Screen Transition Overlay(黑屏淡入淡出)
|
||||
- [ ] `SaveSlotController` + `SaveSlotUI`(主菜单 3 槽存档卡片 UI;架构 10 §7.5)
|
||||
- [ ] `LoadingOverlay`(全屏加载界面;订阅 `EVT_LoadingOverlay` BoolEventChannelSO;架构 10 §8)
|
||||
- [ ] `FloatingDamageText`(对象池驱动的伤害数字飘字;架构 10 §10)
|
||||
- [ ] `ToastNotification`(成就/任务 Toast 弹出;架构 10 §11)
|
||||
- [ ] `InputDeviceIconSwitcher`(键盘/手柄按键图标切换;订阅 `EVT_InputDeviceChanged`;架构 10 §12)
|
||||
- [ ] 所有 Panel 层级配置(`UIManager.OpenPanel` 优先级;⚠️ 非 `ShowPanel`,架构 10 §2)
|
||||
|
||||
#### P4-5:支撑模块(16_SupportingModules)
|
||||
- [ ] `LocalizationManager`(CSV 表格驱动,`GetText(key)`)
|
||||
- [ ] `IPlatformService` ServiceLocator + `SteamPlatformService` + `NullPlatformService`
|
||||
- [ ] `AchievementManager`(通过 `ServiceLocator.Get<IPlatformService>()` 接入平台成就;⚠️ 非直接依赖 `ISteamAchievement`,架构 16 §3)
|
||||
- [ ] `AnalyticsManager` + `IAnalyticsBackend`
|
||||
- [ ] `CheatConsole`(Debug 作弊命令)
|
||||
- [ ] `AntiSoftlockSystem`(卡关检测 + 自动解锁)
|
||||
- [ ] `SpeedrunTimer`(IGT/RTA 计时)
|
||||
|
||||
#### P4-6:编辑器工具(09_EditorExtensions)
|
||||
- [ ] `AddressKeyValidator`(完整版,Build 前校验)
|
||||
- [ ] `AddressReferenceGraphWindow`(`BaseGames/Tools/Asset Reference Graph`;扫描所有 `.cs` 对 `AddressKeys.X` 的引用,标红孤儿 key,导出 CSV;架构 13 §11)
|
||||
- [ ] `DestructibleTile`/`DirectionalDestructible`/`FalseWall`/`PhantomPlate` Gizmo 完整实现
|
||||
- [ ] `NavSurface` 快速烘焙快捷键
|
||||
- [ ] SO 事件频道 Inspector 实时测试按钮(`RaiseInEditor`)
|
||||
- [ ] `CharmEffectDrawer`(`CharmSO.effects` 自定义 PropertyDrawer;架构 16 §4.1)
|
||||
- [ ] `SOValidationRunner` + `IValidatable` 接口(预构建 SO 数据验证;菜单 `Tools/Validate All SOs` + Build Pre-process;架构 16 §10)
|
||||
- [ ] `EventBusMonitorWindow`(Editor Only;运行时事件监控面板,显示订阅者数量;架构 02 §9)
|
||||
- [ ] `EventChainEditorWindow`(`BaseGames/Tools/Event Chain Viewer`;事件链可视化 + 运行时状态着色 + 依赖箭头;架构 14 §13)
|
||||
- [ ] `BossSkillSequenceWindow`(`BaseGames/Tools/Boss Skill Sequence Viewer`;SkillSequenceSO 甘特图时间轴可视化;架构 23 §12)
|
||||
|
||||
---
|
||||
|
||||
## 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 二次确认 |
|
||||
753
Docs/Plan/01_Phase0_Foundation.md
Normal file
753
Docs/Plan/01_Phase0_Foundation.md
Normal file
@@ -0,0 +1,753 @@
|
||||
# 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。**
|
||||
1350
Docs/Plan/02_Phase1_VerticalSlice.md
Normal file
1350
Docs/Plan/02_Phase1_VerticalSlice.md
Normal file
File diff suppressed because it is too large
Load Diff
2781
Docs/Plan/03_Phase2_CoreGameplay.md
Normal file
2781
Docs/Plan/03_Phase2_CoreGameplay.md
Normal file
File diff suppressed because it is too large
Load Diff
3650
Docs/Plan/04_Phase3_WorldProgression.md
Normal file
3650
Docs/Plan/04_Phase3_WorldProgression.md
Normal file
File diff suppressed because it is too large
Load Diff
3227
Docs/Plan/05_Phase4_ContentPolish.md
Normal file
3227
Docs/Plan/05_Phase4_ContentPolish.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user