17 KiB
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结束条件。
推荐状态类命名:
CombatLoadingStateCombatRunningPhaseStateCombatWaitingForPhaseEndStateCombatSettlementStateCombatRewardSelectionStateCombatFinishFormStateCombatWaitingForReturnStateCombatFailedState
实现约束:
- 上述状态类可以作为
CombatScheduler的嵌套类实现,也可以拆成独立文件;但必须只服务于CombatScheduler状态机,不形成独立业务边界。 - 共享数据与共享服务统一收口到
CombatScheduler内部持有的运行时承载体,不允许散落在各状态类中。 - 若
CombatScheduler体量过大,允许在其内部实现中继续拆出:CombatSchedulerRuntimeContext:承载共享运行时字段与共享服务引用CombatSchedulerFlowCoordinator:承载多个状态共用的流程辅助方法
- 上述拆分只属于
CombatScheduler的内部实现细化,不改变CombatScheduler作为唯一状态机边界的职责。 - 所有状态切换只能通过
CombatScheduler.ChangeState(...)完成。 - 状态类不能彼此直接操控。
2.3 EnemyManager(敌人域 Facade)
文件:Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/EnemyManager.cs
长期职责:
- 对状态机提供统一敌人域接口。
- 编排敌人子服务。
- 暴露只读事实:
AliveEnemyCountIsPhaseSpawnCompletedHasAliveBoss
- 在敌人死亡或到家时,通过公共入口向
CombatScheduler上报:OnEnemyDefeated(DREnemy enemy)OnEnemyReachedBase(DREnemy enemy)
长期不负责:
- 不直接给资源入账。
- 不直接扣基地血量。
- 不直接决定状态切换。
3. 状态机模型
3.1 状态列表(目标)
LoadingRunningPhaseWaitingForPhaseEndSettlementRewardSelectionFinishFormWaitingForReturnFailed
说明:
- 正常结束流只有一条状态链:
Settlement -> RewardSelection(可选) -> FinishForm -> WaitingForReturn
- 正常通关、玩家主动结束、基地血量归零都走同一条结束链。
Failed仅用于异常失败,不用于“基地被击破”这类正常战斗失败。
3.2 CombatLoadingState
职责:
- 通过
CombatLoadSession执行地图与基础战斗 UI 加载。 - 从局内资源管理器读取本局快照。
- 组装
MapData并发起ShowEntity(MapEntity)。
约束:
- 只负责加载,不负责初始化局内资源。
- 局内资源必须在进入状态机前初始化完成。
3.3 CombatRunningPhaseState
职责:
- 执行当前
DRLevelPhase的行为。 - 推进
SpawnEntry时序与出怪。 - 管理
EnemySpawnDirector的阶段级初始化与重置。 - 在新 phase 开始时发布:
CombatProcessEventArgsCombatEnemyHpRateChangedEventArgs
退出条件:
- 当前 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真值。 - 持有本局战利品背包。
- 持有本局建塔属性快照。
- 提供只读快照给结束状态链与加载状态使用。
- 发布资源变化事件:
CombatCoinChangedEventArgsCombatBaseHpChangedEventArgs
初始化约束:
- 在进入状态机前完成初始化。
- 由内部从
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 时间信息
AliveEnemyCountIsPhaseSpawnCompletedHasAliveBoss
输出:
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 统一结束链
正常结束统一走:
SettlementRewardSelection(可选)FinishFormWaitingForReturn
7.2 结算上下文
归属:
- 作为
CombatScheduler上的共享字段存在。 Settlement在OnEnter时统一构造。RewardSelection只追加奖励结果。FinishForm与WaitingForReturn只读取。
最小字段集合:
- 最终结算的
Gold/Coin结果 - 待合并的背包快照
BaseHp结算结果- 是否进入过奖励选择
FinishForm所需摘要数据
奖励选择约束:
- 满血奖励选择结果只写入结算上下文。
- 不直接写入局内资源管理器。
- 最终由结束状态链统一合并到玩家背包。
8. 核心不变量(必须保持)
CombatScheduler只做状态机管理与共享运行时收口,不继续吸收具体业务细节。CombatNodeComponent不再持有战斗内资源真值。- 局内
Coin / Gold / BaseHp / Loot Backpack / BuildTowerSnapshots以CombatInRunResourceManager为唯一真值来源。 - 敌人死亡掉落判定以
EnemyDropResolver为唯一判定入口。 - 存活敌人数与
HasAliveBoss以EnemyLifecycleTracker为唯一真值来源。 - Phase 运行时信息与统一结束标记以
PhaseLoopRuntime为唯一真值来源。 PhaseEndType的退出条件以IPhaseEndCondition实现类为唯一判定入口。- 状态切换只能通过
CombatScheduler.ChangeState(...)完成。 - 敌人事件处理入口不直接切状态,状态只能在自己的
OnUpdate中决定迁移。 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 逻辑
优先改结束状态链:
CombatSettlementStateCombatRewardSelectionStateCombatFinishFormStateCombatWaitingForReturnState
10.7 新增战斗流程事件
优先由具体状态或局内资源管理器发布,不要回流到 CombatNodeComponent。
11. 代码变更检查清单(PR 自检)
- 新逻辑是否落在正确的状态或服务,而不是继续堆进
CombatScheduler本体? CombatNodeComponent是否仍然保持为轻量入口 Facade?- 是否破坏了局内资源、掉落判定、phase runtime、phase end 判定的唯一真值来源?
- 敌人事件处理是否仍然只做公共副作用,而不直接切状态?
- 状态迁移是否仍然统一走
ChangeState(...)? MapEntity是否仍然只通过MapData + Event获取战斗上下文?- 清理是否仍按“敌人 / 地图基础 UI / 结算 UI / 运行时数据”分工执行?