geometry-tower-defense/docs/CombatNodeArchitecture.md

534 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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` 体量过大,允许在其内部实现中继续拆出:
- `CombatSchedulerRuntime`:承载共享运行时字段与共享服务引用
- `CombatSchedulerCoordinator`:承载多个状态共用的流程辅助方法
- 上述拆分只属于 `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 命名后缀词典
- `Scheduler`:只用于状态机边界或阶段推进总控,例如 `CombatScheduler`
- `Manager`:只用于子域 Facade/聚合入口,例如 `EnemyManager`
- `Coordinator`:只用于跨状态、跨服务的流程编排,不持有独立业务真值。
- `Service`:只用于聚焦业务行为,不承担框架事件桥接或异步句柄跟踪。
- `Session`:只用于一次加载/交互过程的生命周期对象。
- `Bridge`:只用于框架边界适配器。
- `Runtime`:只用于运行时可变状态承载。
- `Context`:只用于被动数据包或共享上下文。
- `Result`:只用于动作输出或结算产出;若外围服务名已表达动作语义,不再重复动作前缀。
- `Flags`:只用于聚合布尔控制项,不承载流程编排逻辑。
- `Resolver`:只用于映射、查找、判定、解析职责。
- `Tracker`:只用于跟踪运行中的实体或事实真值。
- `Port`:只用于向内部状态或 use case 暴露的受限宿主接口。
### 4.2 CombatLoadSession
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatLoadSession.cs`
长期定位:
- 长期保留的独立加载服务。
- 专门负责地图与战斗内基础 UI 的加载/清理。
职责:
- 加载地图实体。
- 打开/关闭 `CombatInfoForm`
- 跟踪加载成功/失败状态。
- 对外提供 `CurrentMap``IsReady`
### 4.2.x CombatSchedulerRuntime / CombatSchedulerCoordinator实现细化
当前实现允许:
-`CombatSchedulerRuntime` 承载所有状态共享的运行时字段与共享服务引用。
-`CombatSchedulerCoordinator` 承载多个状态共用的流程辅助逻辑。
约束:
- 两者都必须由 `CombatScheduler` 持有并统一管理生命周期。
- 两者都不替代 `CombatScheduler` 对外暴露状态机边界。
- `Runtime` 不负责状态切换。
- `Coordinator` 不持有独立业务真值,只能围绕共享运行时做编排辅助。
- 状态类只允许通过 `Runtime + Coordinator` 访问共享状态与共享流程,不应再直接耦合其他状态实现细节。
### 4.3 PhaseLoopRuntime
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/PhaseLoopRuntime.cs`
长期定位:
- 长期保留的独立 phase runtime 服务。
职责:
- 维护当前 `DRLevelPhase`
- 维护 `DisplayPhaseIndex`、`PhaseCount`。
- 维护统一的 phase 时间基准,例如 `phaseElapsedTime``phaseStartTime`
- 负责进入下一 phase。
- 持有统一“请求结束战斗”标记。
约束:
- 只做 phase 运行时数据管理,不直接切状态。
### 4.4 CombatRunResourceStore
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatRunResourceStore.cs`
目标职责:
- 持有本局 `Coin` 真值。
- 持有本局累计 `Gold` 真值。
- 持有本局 `BaseHp` 真值。
- 持有本局战利品背包。
- 持有本局建塔属性快照。
- 提供只读快照给结束状态链与加载状态使用。
- 发布资源变化事件:
- `CombatCoinChangedEventArgs`
- `CombatBaseHpChangedEventArgs`
初始化约束:
- 在进入状态机前完成初始化。
- 由内部从 `PlayerInventory` 获取并缓存本局建塔快照。
事件约束:
- `Coin / BaseHp` 变化事件同时携带“当前值”和“变化量”。
- `Gold` 只是结算累计值,不要求战斗内实时事件驱动。
### 4.5 EnemyDropResolver
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/EnemyDrop/EnemyDropResolver.cs`
目标职责:
- 只负责敌人死亡后的掉落判定。
- 输入:
- `DREnemy`
- 当前阶段索引
- 当前主题或关卡上下文
- 输出:
- 掉落结果对象(`coin / gold / loot`
约束:
- 不直接修改资源状态。
- 不直接读取 `CombatNodeComponent`、`MapEntity`、`EnemyManager` 内部状态。
### 4.6 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 EnemySpawnPathResolver
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/EnemySpawnPathResolver.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 EnemyConfigProvider
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/EnemyConfigProvider.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` 所需摘要数据
命名约束:
- 结算上下文中的布尔控制项统一收口到 `Flags`,不再使用 `Flow` 命名。
奖励选择约束:
- 满血奖励选择结果只写入结算上下文。
- 不直接写入局内资源管理器。
- 最终由结束状态链统一合并到玩家背包。
---
## 8. 核心不变量(必须保持)
1. `CombatScheduler` 只做状态机管理与共享运行时收口,不继续吸收具体业务细节。
2. `CombatNodeComponent` 不再持有战斗内资源真值。
3. 局内 `Coin / Gold / BaseHp / Loot Backpack / BuildTowerSnapshots``CombatRunResourceStore` 为唯一真值来源。
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 新增战斗内资源或建塔快照规则
优先改 `CombatRunResourceStore`,不要回流到 `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 / 运行时数据”分工执行?