# CombatNode 设计规范(开发约束) 最后更新:2026-03-12 ## 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`:只用于聚焦业务行为,不承担框架事件桥接或异步句柄跟踪。 - `Calculator`:只用于纯计算与结果组装,不直接提交状态或驱动 UI。 - `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 InventoryGenerationComponent 文件:`Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/InventoryGenerationComponent.cs` 目标职责: - 作为局外组件产出的统一运行时入口。 - 对外提供: - `BuildShopGoods(...)` - `ResolveEnemyDrop(...)` - `BuildRewardCandidates(...)` - 在内部编排: - `DropPoolRoller` - `RewardCandidateBuilder` - `ComponentItemFactory` 约束: - `CombatNode` 域不直接持有或复制组件产出规则。 - `CombatScheduler` 与结算状态链只调用统一入口,不直接访问掉落池滚动或组件实例构造细节。 - `InventoryGenerationComponent` 负责组件实例生成、Tag 随机上下文以及 `runSeed/sequenceIndex` 相关上下文。 ### 4.6 CombatSettlementCalculator 文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatSettlementCalculator.cs` 目标职责: - 只负责结算计算与 `CombatSettlementContext` 组装。 - 负责基地血量奖励、奖励选择准入、奖励背包快照与耐久扣减目标计算。 约束: - 不直接并包到玩家库存。 - 不直接打开 UI。 - 不承担奖励候选生成。 ### 4.7 CombatSettlementCommitter 文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatSettlementCommitter.cs` 目标职责: - 只负责把结算结果提交到玩家库存。 - 负责结算背包并包与延迟耐久扣减落地。 约束: - 不重新计算结算上下文。 - 不直接生成奖励候选或打开 UI。 ### 4.8 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` 公共层负责处理敌人事件的通用副作用: - 击杀:调用 `GameEntry.InventoryGeneration.ResolveEnemyDrop(...)`,再调用局内资源管理器入账。 - 到家:调用局内资源管理器扣减 `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. 组件产出规则以 `InventoryGenerationComponent` 为统一运行时入口;战斗掉落与奖励候选都通过它生成。 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 新增敌人掉落规则 优先改 `InventoryGenerationComponent` 及其下层规则模块,不要在 `EnemyManager`、`CombatScheduler` 或状态类里直接计算掉落。 ### 10.3.x 新增奖励候选规则 优先改 `InventoryGenerationComponent`、`RewardCandidateBuilder` 或 `DropPoolRoller`,不要在结算状态链里复制一套候选生成规则。 ### 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 / 运行时数据”分工执行?