515 lines
17 KiB
Markdown
515 lines
17 KiB
Markdown
# CombatNode 设计规范(开发约束)
|
||
|
||
最后更新:2026-03-06
|
||
|
||
## 1. 适用范围与目标
|
||
|
||
本文描述 `CombatNode` 域的后续开发标准。
|
||
|
||
说明:
|
||
- 本文是“目标架构约束”,不要求当前代码已经完全达成。
|
||
- 后续新增功能、重构、拆分类、review 职责边界时,以本文为准。
|
||
- 如果当前实现与本文不一致,新增代码优先向本文收敛,而不是继续扩大旧结构。
|
||
|
||
核心目标:
|
||
- `CombatScheduler` 收敛为“状态机管理器”,不再继续堆积加载、结算、奖励选择等业务细节。
|
||
- 战斗内资源收口到独立资源服务,由内部管理,不再由 `CombatNodeComponent` 直接持有真值。
|
||
- `MapEntity` 通过 `MapData + Event` 获取战斗上下文,不反查 `CombatNode` 域内部状态。
|
||
|
||
---
|
||
|
||
## 2. 架构总览
|
||
|
||
### 2.1 CombatNodeComponent(入口 Facade)
|
||
|
||
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatNodeComponent.cs`
|
||
|
||
长期职责:
|
||
- 读取并缓存 `DRLevel / DRLevelPhase / DRLevelSpawnEntry`。
|
||
- 按主题筛选关卡。
|
||
- 启动/停止 `CombatScheduler`。
|
||
- 对外暴露只读运行时属性。
|
||
- 提供少量用户入口,例如 `StartCombat`、`TryEndCombatByPlayer`。
|
||
|
||
长期不负责:
|
||
- 不直接持有 `Coin / Gold / BaseHp / Loot Backpack` 的真值。
|
||
- 不直接缓存本局建塔属性快照。
|
||
- 不直接发布战斗流程事件。
|
||
- 不直接处理敌人掉落、结算、奖励选择、地图加载。
|
||
|
||
### 2.2 CombatScheduler(状态机管理器)
|
||
|
||
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs`
|
||
|
||
长期职责:
|
||
- 持有共享运行时数据与共享服务实例。
|
||
- 管理状态实例。
|
||
- 提供统一的 `ChangeState(...)` 状态迁移入口。
|
||
- 提供敌人事件的公共处理入口。
|
||
- 作为状态机生命周期边界,统一做运行时重置。
|
||
|
||
长期不负责:
|
||
- 不直接硬编码加载流程。
|
||
- 不直接硬编码结算流程。
|
||
- 不直接硬编码奖励选择 UI 逻辑。
|
||
- 不直接硬编码 `PhaseEndType` 结束条件。
|
||
|
||
推荐状态类命名:
|
||
- `CombatLoadingState`
|
||
- `CombatRunningPhaseState`
|
||
- `CombatWaitingForPhaseEndState`
|
||
- `CombatSettlementState`
|
||
- `CombatRewardSelectionState`
|
||
- `CombatFinishFormState`
|
||
- `CombatWaitingForReturnState`
|
||
- `CombatFailedState`
|
||
|
||
实现约束:
|
||
- 上述状态类可以作为 `CombatScheduler` 的嵌套类实现,也可以拆成独立文件;但必须只服务于 `CombatScheduler` 状态机,不形成独立业务边界。
|
||
- 共享数据与共享服务统一收口到 `CombatScheduler` 内部持有的运行时承载体,不允许散落在各状态类中。
|
||
- 若 `CombatScheduler` 体量过大,允许在其内部实现中继续拆出:
|
||
- `CombatSchedulerRuntimeContext`:承载共享运行时字段与共享服务引用
|
||
- `CombatSchedulerFlowCoordinator`:承载多个状态共用的流程辅助方法
|
||
- 上述拆分只属于 `CombatScheduler` 的内部实现细化,不改变 `CombatScheduler` 作为唯一状态机边界的职责。
|
||
- 所有状态切换只能通过 `CombatScheduler.ChangeState(...)` 完成。
|
||
- 状态类不能彼此直接操控。
|
||
|
||
### 2.3 EnemyManager(敌人域 Facade)
|
||
|
||
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/EnemyManager.cs`
|
||
|
||
长期职责:
|
||
- 对状态机提供统一敌人域接口。
|
||
- 编排敌人子服务。
|
||
- 暴露只读事实:
|
||
- `AliveEnemyCount`
|
||
- `IsPhaseSpawnCompleted`
|
||
- `HasAliveBoss`
|
||
- 在敌人死亡或到家时,通过公共入口向 `CombatScheduler` 上报:
|
||
- `OnEnemyDefeated(DREnemy enemy)`
|
||
- `OnEnemyReachedBase(DREnemy enemy)`
|
||
|
||
长期不负责:
|
||
- 不直接给资源入账。
|
||
- 不直接扣基地血量。
|
||
- 不直接决定状态切换。
|
||
|
||
---
|
||
|
||
## 3. 状态机模型
|
||
|
||
### 3.1 状态列表(目标)
|
||
|
||
- `Loading`
|
||
- `RunningPhase`
|
||
- `WaitingForPhaseEnd`
|
||
- `Settlement`
|
||
- `RewardSelection`
|
||
- `FinishForm`
|
||
- `WaitingForReturn`
|
||
- `Failed`
|
||
|
||
说明:
|
||
- 正常结束流只有一条状态链:
|
||
- `Settlement -> RewardSelection(可选) -> FinishForm -> WaitingForReturn`
|
||
- 正常通关、玩家主动结束、基地血量归零都走同一条结束链。
|
||
- `Failed` 仅用于异常失败,不用于“基地被击破”这类正常战斗失败。
|
||
|
||
### 3.2 CombatLoadingState
|
||
|
||
职责:
|
||
- 通过 `CombatLoadSession` 执行地图与基础战斗 UI 加载。
|
||
- 从局内资源管理器读取本局快照。
|
||
- 组装 `MapData` 并发起 `ShowEntity(MapEntity)`。
|
||
|
||
约束:
|
||
- 只负责加载,不负责初始化局内资源。
|
||
- 局内资源必须在进入状态机前初始化完成。
|
||
|
||
### 3.3 CombatRunningPhaseState
|
||
|
||
职责:
|
||
- 执行当前 `DRLevelPhase` 的行为。
|
||
- 推进 `SpawnEntry` 时序与出怪。
|
||
- 管理 `EnemySpawnDirector` 的阶段级初始化与重置。
|
||
- 在新 phase 开始时发布:
|
||
- `CombatProcessEventArgs`
|
||
- `CombatEnemyHpRateChangedEventArgs`
|
||
|
||
退出条件:
|
||
- 当前 phase 的所有 `SpawnEntry` 已执行完毕时,进入 `WaitingForPhaseEnd`。
|
||
- 若共享“结束战斗请求标记”已置位,也可结束当前运行态并转入正常结束链。
|
||
|
||
不负责:
|
||
- 不根据 `PhaseEndType` 判断 phase 是否真正结束。
|
||
- 不直接根据 `BaseHp` 或敌人死亡事件切状态。
|
||
|
||
### 3.4 CombatWaitingForPhaseEndState
|
||
|
||
职责:
|
||
- 不再生成新敌人。
|
||
- 根据 `PhaseEndType` 判断当前 phase 是否结束。
|
||
|
||
约束:
|
||
- `PhaseEndType` 的判断由独立判定服务负责,不在状态内硬编码。
|
||
- 每种 `PhaseEndType` 对应一个实现类。
|
||
- 该判定服务为本状态专用,不作为全局共享服务常驻在 `CombatScheduler` 上。
|
||
|
||
### 3.5 CombatSettlementState
|
||
|
||
职责:
|
||
- 进入时统一构造结算上下文。
|
||
- 根据共享资源状态完成结算修正。
|
||
- 决定后续进入 `RewardSelection` 还是 `FinishForm`。
|
||
|
||
负责的逻辑包括:
|
||
- 基地血量奖励/惩罚。
|
||
- 满血奖励选择的准入判断。
|
||
- 生成最终展示摘要。
|
||
- 准备待合并的结算背包快照。
|
||
|
||
约束:
|
||
- 不依赖单独的 `CombatEndReason` 字段。
|
||
- `BaseHp <= 0` 表示基地被击破。
|
||
- 正常通关与玩家主动结束在结算产出上不区分原因。
|
||
|
||
### 3.6 CombatRewardSelectionState
|
||
|
||
职责:
|
||
- 绑定、配置、打开、关闭 `RewardSelectForm`。
|
||
- 处理奖励选择过程。
|
||
- 将奖励选择结果写入“结束状态链持有的结算上下文”。
|
||
|
||
约束:
|
||
- 不重新判断“是否应该出现奖励选择”。
|
||
- 只处理选择过程本身。
|
||
|
||
### 3.7 CombatFinishFormState
|
||
|
||
职责:
|
||
- 绑定、配置、打开、关闭 `CombatFinishForm`。
|
||
- 读取结算上下文并展示最终结算结果。
|
||
|
||
### 3.8 CombatWaitingForReturnState
|
||
|
||
职责:
|
||
- 等待玩家从结算返回。
|
||
- 完成地图与战斗基础 UI 清理。
|
||
- 完成正常退出收尾。
|
||
- 在整场战斗真正退出时发布 `NodeCompleteEventArgs`。
|
||
|
||
### 3.9 CombatFailedState
|
||
|
||
职责:
|
||
- 表示异常失败。
|
||
- 保存并展示错误信息。
|
||
- 执行异常收尾与剩余资源回收。
|
||
|
||
约束:
|
||
- `Failed` 只处理异常路径。
|
||
- “基地血量为 0”不进入 `Failed`。
|
||
|
||
---
|
||
|
||
## 4. 共享服务与推荐命名
|
||
|
||
### 4.1 CombatLoadSession
|
||
|
||
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatLoadSession.cs`
|
||
|
||
长期定位:
|
||
- 长期保留的独立加载服务。
|
||
- 专门负责地图与战斗内基础 UI 的加载/清理。
|
||
|
||
职责:
|
||
- 加载地图实体。
|
||
- 打开/关闭 `CombatInfoForm`。
|
||
- 跟踪加载成功/失败状态。
|
||
- 对外提供 `CurrentMap` 与 `IsReady`。
|
||
|
||
### 4.1.x CombatSchedulerRuntimeContext / CombatSchedulerFlowCoordinator(实现细化)
|
||
|
||
当前实现允许:
|
||
- 用 `CombatSchedulerRuntimeContext` 承载所有状态共享的运行时字段与共享服务引用。
|
||
- 用 `CombatSchedulerFlowCoordinator` 承载多个状态共用的流程辅助逻辑。
|
||
|
||
约束:
|
||
- 两者都必须由 `CombatScheduler` 持有并统一管理生命周期。
|
||
- 两者都不替代 `CombatScheduler` 对外暴露状态机边界。
|
||
- `RuntimeContext` 不负责状态切换。
|
||
- `FlowCoordinator` 不持有独立业务真值,只能围绕共享运行时做编排辅助。
|
||
- 状态类只允许通过 `RuntimeContext + FlowCoordinator` 访问共享状态与共享流程,不应再直接耦合其他状态实现细节。
|
||
|
||
### 4.2 PhaseLoopRuntime
|
||
|
||
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/PhaseLoopRuntime.cs`
|
||
|
||
长期定位:
|
||
- 长期保留的独立 phase runtime 服务。
|
||
|
||
职责:
|
||
- 维护当前 `DRLevelPhase`。
|
||
- 维护 `DisplayPhaseIndex`、`PhaseCount`。
|
||
- 维护统一的 phase 时间基准,例如 `phaseElapsedTime` 或 `phaseStartTime`。
|
||
- 负责进入下一 phase。
|
||
- 持有统一“请求结束战斗”标记。
|
||
|
||
约束:
|
||
- 只做 phase 运行时数据管理,不直接切状态。
|
||
|
||
### 4.3 CombatInRunResourceManager(推荐命名)
|
||
|
||
当前相关文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatResourceManager.cs`
|
||
|
||
目标职责:
|
||
- 持有本局 `Coin` 真值。
|
||
- 持有本局累计 `Gold` 真值。
|
||
- 持有本局 `BaseHp` 真值。
|
||
- 持有本局战利品背包。
|
||
- 持有本局建塔属性快照。
|
||
- 提供只读快照给结束状态链与加载状态使用。
|
||
- 发布资源变化事件:
|
||
- `CombatCoinChangedEventArgs`
|
||
- `CombatBaseHpChangedEventArgs`
|
||
|
||
初始化约束:
|
||
- 在进入状态机前完成初始化。
|
||
- 由内部从 `PlayerInventory` 获取并缓存本局建塔快照。
|
||
|
||
事件约束:
|
||
- `Coin / BaseHp` 变化事件同时携带“当前值”和“变化量”。
|
||
- `Gold` 只是结算累计值,不要求战斗内实时事件驱动。
|
||
|
||
### 4.4 EnemyDropResolver(推荐命名)
|
||
|
||
当前相关文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatResourceManager.cs`
|
||
|
||
目标职责:
|
||
- 只负责敌人死亡后的掉落判定。
|
||
- 输入:
|
||
- `DREnemy`
|
||
- 当前阶段索引
|
||
- 当前主题或关卡上下文
|
||
- 输出:
|
||
- 掉落结果对象(`coin / gold / loot`)
|
||
|
||
约束:
|
||
- 不直接修改资源状态。
|
||
- 不直接读取 `CombatNodeComponent`、`MapEntity`、`EnemyManager` 内部状态。
|
||
|
||
### 4.5 IPhaseEndCondition(推荐命名)
|
||
|
||
目标职责:
|
||
- 作为 `PhaseEndType` 判定接口。
|
||
- 每种 `PhaseEndType` 对应一个实现类。
|
||
|
||
只读输入:
|
||
- 当前 `DRLevelPhase`
|
||
- phase 时间信息
|
||
- `AliveEnemyCount`
|
||
- `IsPhaseSpawnCompleted`
|
||
- `HasAliveBoss`
|
||
|
||
输出:
|
||
- `bool ShouldExit`
|
||
|
||
约束:
|
||
- 不直接切状态。
|
||
- 不直接发事件。
|
||
- 不直接改资源。
|
||
|
||
---
|
||
|
||
## 5. EnemyManager 子服务边界
|
||
|
||
### 5.1 EnemySpawnDirector
|
||
|
||
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/EnemySpawnDirector.cs`
|
||
|
||
职责:
|
||
- 长期保留为独立服务。
|
||
- 基于 `spawnEntries + phase time` 计算当前应执行的刷怪行为。
|
||
- 提供“当前 phase 的 `SpawnEntry` 是否已全部执行完”的事实。
|
||
|
||
生命周期:
|
||
- 由 `CombatRunningPhaseState` 在状态进入/退出时初始化与重置。
|
||
|
||
### 5.2 SpawnerResolver
|
||
|
||
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/SpawnerResolver.cs`
|
||
|
||
职责:
|
||
- 缓存当前地图可用 `Spawner`。
|
||
- 提供出生点与路径解析。
|
||
|
||
### 5.3 EnemyLifecycleTracker
|
||
|
||
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/EnemyLifecycleTracker.cs`
|
||
|
||
职责:
|
||
- 维护 `AliveEnemyCount` 真值。
|
||
- 维护 `HasAliveBoss` 真值。
|
||
- 追踪本局 tracked 敌人。
|
||
- 导出 tracked ids 供清场使用。
|
||
|
||
Boss 识别规则:
|
||
- Boss 身份由 `DRLevelSpawnEntry.EntryType == Boss` 决定。
|
||
- 不由 `DREnemy` 自身类型决定。
|
||
|
||
### 5.4 EnemyConfigService
|
||
|
||
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/EnemyConfigService.cs`
|
||
|
||
职责:
|
||
- 读取 `DREnemy`。
|
||
- 处理默认配置兜底。
|
||
- 计算循环周目下的基础血量倍率。
|
||
|
||
---
|
||
|
||
## 6. 事件与数据流规范
|
||
|
||
### 6.1 MapEntity 与 Combat 域解耦
|
||
|
||
必须保持:
|
||
- `MapEntity` 不直接查询 `CombatNodeComponent` 的运行时资源字段。
|
||
- 战斗初始上下文通过 `MapData` 注入。
|
||
- `Coin` 初值通过 `MapData` 传入。
|
||
- 后续 `Coin` 变化通过 `CombatCoinChangedEventArgs` 同步。
|
||
- `TowerStatsData` 等本局不变量直接放进 `MapData`。
|
||
- `MapEntity` 不反查 Combat 域内部服务。
|
||
|
||
`MapData` 组装规则:
|
||
- 由 `CombatLoadingState` 从局内资源管理器读取快照。
|
||
- 由 `CombatLoadingState` 打包成 `MapData` 后再 `ShowEntity(MapEntity)`。
|
||
|
||
### 6.2 敌人事件处理
|
||
|
||
统一边界:
|
||
- `EnemyManager` 只上报:
|
||
- `OnEnemyDefeated(DREnemy enemy)`
|
||
- `OnEnemyReachedBase(DREnemy enemy)`
|
||
- `CombatScheduler` 公共层负责处理敌人事件的通用副作用:
|
||
- 击杀:调用 `EnemyDropResolver`,再调用局内资源管理器入账。
|
||
- 到家:调用局内资源管理器扣减 `BaseHp`。
|
||
|
||
约束:
|
||
- 敌人事件入口不直接调用 `ChangeState(...)`。
|
||
- `BaseHp <= 0` 的判断由当前状态在 `OnUpdate` 中处理。
|
||
|
||
### 6.3 战斗流程事件
|
||
|
||
发布边界:
|
||
- 资源变化事件由局内资源管理器发布。
|
||
- 流程/阶段事件由状态机或具体状态发布。
|
||
|
||
发布时间:
|
||
- `NodeEnterEventArgs`:`Loading` 完成并正式进入首个 `RunningPhase` 时。
|
||
- `CombatProcessEventArgs`:新 phase 的 `RunningPhase.OnEnter`。
|
||
- `CombatEnemyHpRateChangedEventArgs`:与 `CombatProcessEventArgs` 同时发布。
|
||
- `NodeCompleteEventArgs`:`WaitingForReturn` 完成清理、整场战斗真正退出时。
|
||
|
||
---
|
||
|
||
## 7. 结束链与结算上下文
|
||
|
||
### 7.1 统一结束链
|
||
|
||
正常结束统一走:
|
||
- `Settlement`
|
||
- `RewardSelection`(可选)
|
||
- `FinishForm`
|
||
- `WaitingForReturn`
|
||
|
||
### 7.2 结算上下文
|
||
|
||
归属:
|
||
- 作为 `CombatScheduler` 上的共享字段存在。
|
||
- `Settlement` 在 `OnEnter` 时统一构造。
|
||
- `RewardSelection` 只追加奖励结果。
|
||
- `FinishForm` 与 `WaitingForReturn` 只读取。
|
||
|
||
最小字段集合:
|
||
- 最终结算的 `Gold/Coin` 结果
|
||
- 待合并的背包快照
|
||
- `BaseHp` 结算结果
|
||
- 是否进入过奖励选择
|
||
- `FinishForm` 所需摘要数据
|
||
|
||
奖励选择约束:
|
||
- 满血奖励选择结果只写入结算上下文。
|
||
- 不直接写入局内资源管理器。
|
||
- 最终由结束状态链统一合并到玩家背包。
|
||
|
||
---
|
||
|
||
## 8. 核心不变量(必须保持)
|
||
|
||
1. `CombatScheduler` 只做状态机管理与共享运行时收口,不继续吸收具体业务细节。
|
||
2. `CombatNodeComponent` 不再持有战斗内资源真值。
|
||
3. 局内 `Coin / Gold / BaseHp / Loot Backpack / BuildTowerSnapshots` 以 `CombatInRunResourceManager` 为唯一真值来源。
|
||
4. 敌人死亡掉落判定以 `EnemyDropResolver` 为唯一判定入口。
|
||
5. 存活敌人数与 `HasAliveBoss` 以 `EnemyLifecycleTracker` 为唯一真值来源。
|
||
6. Phase 运行时信息与统一结束标记以 `PhaseLoopRuntime` 为唯一真值来源。
|
||
7. `PhaseEndType` 的退出条件以 `IPhaseEndCondition` 实现类为唯一判定入口。
|
||
8. 状态切换只能通过 `CombatScheduler.ChangeState(...)` 完成。
|
||
9. 敌人事件处理入口不直接切状态,状态只能在自己的 `OnUpdate` 中决定迁移。
|
||
10. `MapEntity` 通过 `MapData + Event` 获取战斗上下文,不反查 Combat 域内部运行时。
|
||
|
||
---
|
||
|
||
## 9. 清理职责
|
||
|
||
- 敌人清理:`EnemyManager`,且只清理本局 tracked 敌人。
|
||
- 地图与战斗基础 UI 清理:`CombatLoadSession`。
|
||
- 结算/奖励 UI 清理:结束状态链或 `Failed` 状态。
|
||
- 运行时数据重置:`CombatScheduler` 在状态机生命周期边界统一执行。
|
||
|
||
---
|
||
|
||
## 10. 扩展开发规范
|
||
|
||
### 10.1 新增刷怪类型或 SpawnEntry 行为
|
||
|
||
优先改 `EnemySpawnDirector`,不要把时序细节塞进 `CombatRunningPhaseState`。
|
||
|
||
### 10.2 新增 Phase 结束条件
|
||
|
||
新增 `IPhaseEndCondition` 实现类,不要在 `CombatWaitingForPhaseEndState` 里写大分支。
|
||
|
||
### 10.3 新增敌人掉落规则
|
||
|
||
优先改 `EnemyDropResolver`,不要在 `EnemyManager` 或状态类里直接计算掉落。
|
||
|
||
### 10.4 新增战斗内资源或建塔快照规则
|
||
|
||
优先改 `CombatInRunResourceManager`,不要回流到 `CombatNodeComponent`。
|
||
|
||
### 10.5 新增地图/战斗基础 UI 加载规则
|
||
|
||
优先改 `CombatLoadSession` 或 `CombatLoadingState`,不要把加载细节塞回 `CombatScheduler` 本体。
|
||
|
||
### 10.6 新增强化结算、奖励选择、结算 UI 逻辑
|
||
|
||
优先改结束状态链:
|
||
- `CombatSettlementState`
|
||
- `CombatRewardSelectionState`
|
||
- `CombatFinishFormState`
|
||
- `CombatWaitingForReturnState`
|
||
|
||
### 10.7 新增战斗流程事件
|
||
|
||
优先由具体状态或局内资源管理器发布,不要回流到 `CombatNodeComponent`。
|
||
|
||
---
|
||
|
||
## 11. 代码变更检查清单(PR 自检)
|
||
|
||
1. 新逻辑是否落在正确的状态或服务,而不是继续堆进 `CombatScheduler` 本体?
|
||
2. `CombatNodeComponent` 是否仍然保持为轻量入口 Facade?
|
||
3. 是否破坏了局内资源、掉落判定、phase runtime、phase end 判定的唯一真值来源?
|
||
4. 敌人事件处理是否仍然只做公共副作用,而不直接切状态?
|
||
5. 状态迁移是否仍然统一走 `ChangeState(...)`?
|
||
6. `MapEntity` 是否仍然只通过 `MapData + Event` 获取战斗上下文?
|
||
7. 清理是否仍按“敌人 / 地图基础 UI / 结算 UI / 运行时数据”分工执行?
|