# CombatNode 架构摘要 最后更新:2026-02-28 ## 1. 目标与边界 CombatNode 子系统的目标是把“战斗节点”拆成三个稳定层: - `CombatNodeComponent`:节点入口与配置缓存(门面层) - `CombatScheduler`:单局战斗状态机(编排层) - `EnemyManager`:刷怪与敌人生命周期(执行层) 边界约束: - 只负责战斗节点,不负责菜单流程切换,不负责 UI 细节。 - 只依赖数据表和 Entity 系统,不直接持有场景流程 FSM。 - 地图寻路由 `MapEntity` 提供能力,敌人移动由 `EnemyEntity` 自治。 --- ## 2. 模块职责划分 ## 2.1 CombatNodeComponent(门面层) 文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatNodeComponent.cs` 职责: - 读取并缓存 `DRLevel / DRLevelPhase / DRLevelSpawnEntry`。 - 按主题筛选关卡,维护 `Level -> Phase -> SpawnEntry` 映射。 - 提供 `OnInit / StartCombat / OnUpdate / OnShutdown` 外部接口。 - 触发节点事件: - `NodeEnterEventArgs` - `NodeCompleteEventArgs` 不做的事: - 不做阶段推进。 - 不做刷怪时序。 - 不做实体显示/隐藏细节。 ## 2.2 CombatScheduler(编排层) 文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler.cs` 职责: - 管理战斗状态机:`Idle -> WaitingForMap -> RunningPhase -> Completed/Failed`。 - 加载地图实体,接收地图 show/hide 成功失败事件。 - 按 phase 结束条件推进到下一阶段。 - 在开始新战斗或销毁时做统一清场(地图 + 本局敌人)。 不做的事: - 不直接计算每条刷怪规则的触发细节(交给 `EnemyManager`)。 ## 2.3 EnemyManager(执行层) 文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager.cs` 职责: - 将 `DRLevelSpawnEntry` 转为运行时任务(stream/burst/boss)。 - 按时间推进刷怪任务并生成 `EnemyData`。 - 维护本局敌人数量 `AliveEnemyCount`。 - 维护“本局生成敌人 ID 集合”,用于准确计数与清场。 关键点: - 生命周期归属按 `entityId` 跟踪,不再按实体组名粗粒度统计。 - 清场可覆盖“已加载 + 加载中”的本局敌人。 --- ## 3. 关键实体职责 ## 3.1 MapEntity 文件:`Assets/GameMain/Scripts/Entity/EntityLogic/MapEntity.cs` 职责: - 读取 Tilemap,识别 Path/Foundation 格子。 - 为每个 `Spawner` 缓存默认可达路径。 - 提供: - `TryGetDefaultPathCells` - `TryFindPathCells` - `TryFindPathWorldPoints` ## 3.2 EnemyEntity 文件:`Assets/GameMain/Scripts/Entity/EntityLogic/EnemyEntity.cs` 职责: - 按路径点移动。 - 到达终点后调用 `HideEntity` 自行退出。 --- ## 4. 运行时时序(简版) 1. 菜单触发战斗:`TestMenuFormController -> GameEntry.CombatNode.StartCombat()` 2. `CombatNodeComponent.StartCombat()` 选关并把关卡配置交给 `CombatScheduler.Start()` 3. `CombatScheduler.Start()` 先清理上局残留,再 `ShowMap(...)`,状态进入 `WaitingForMap` 4. 地图加载成功事件到达,`_currentMap` 就绪 5. `BeginNextPhase()` 进入 `RunningPhase`,`EnemyManager.BeginPhase(...)` 6. 每帧 `OnUpdate`: - `CombatScheduler` 更新时间与结束条件 - `EnemyManager` 推进刷怪任务并创建敌人 7. 当前 phase 满足结束条件,`CompleteCurrentPhase()`,然后进入下一 phase 8. 全 phase 完成后隐藏地图,`GameEntry.CombatNode.EndCombat()`,抛 `NodeComplete` --- ## 5. 数据契约与 ID 规则 数据来源: - `DRLevel` - `DRLevelPhase` - `DRLevelSpawnEntry` 当前约定(依赖 ID 编码): - `levelId = phaseId / 1000` - `phaseId = spawnEntryId / 1000` 影响: - 新增配置时必须遵守该编码,否则 phase 和 entry 无法被正确归属。 建议: - 若后续改数据结构,优先改成显式外键字段,减少 ID 推导耦合。 --- ## 6. 结束条件模型 枚举:`PhaseEndType` - `TimeElapsed`:按 `EndParam`(可解析 float)或 `DurationSeconds` - `EnemiesCleared`:`IsPhaseSpawnCompleted && AliveEnemyCount <= 0` - `BossDead`:当前与 `EnemiesCleared` 同判定 - `None`:有 duration 走 duration,否则退化为清怪判定 实现点:`CombatScheduler.ShouldEndCurrentPhase()` --- ## 7. 生命周期与清场策略(当前实现) ## 7.1 地图生命周期 - 通过 `ShowMap` 异步加载。 - 通过 `ShowEntitySuccess/Failure` 绑定运行态地图引用。 - 完成关卡时隐藏地图。 - 新战斗开始和调度器销毁前都会尝试清理地图实体。 ## 7.2 敌人生命周期 - 创建时登记 `entityId` 到 `_trackedEnemyEntityIds`。 - `ShowEntitySuccess` 仅对 tracked id 计数。 - `HideEntityComplete` 仅对 tracked id 减计数并移除跟踪。 - 清场时按 tracked id 隐藏(覆盖加载中和已加载实体)。 ## 7.3 设计目标 - 防止跨局残留实体污染新局状态。 - 防止其他系统 `Enemy` 组实体干扰战斗计数。 --- ## 8. 扩展指南(按需求) ## 8.1 新增阶段结束条件 - 改 `PhaseEndType` 枚举。 - 在 `ShouldEndCurrentPhase()` 增加分支。 ## 8.2 新增刷怪条目类型 - 改 `EntryType` 枚举。 - 在 `EnemyManager.BuildSpawnRuntime()` 与 `UpdateSpawnRuntimes()` 增加处理。 ## 8.3 改选关策略 - 当前是随机选关:`TrySelectRandomLevel()`。 - 可改为按难度、解锁进度、权重池。 ## 8.4 动态阻挡/重算路径 - 当前 `SpawnEnemies` 调 `TryFindPathWorldPoints(spawner, null, ...)`。 - 若支持塔阻挡,传入 `blockedCells` 并处理“无路可走”策略。 ## 8.5 结算与奖励 - 推荐挂在 `CombatScheduler` 完成分支(所有 phase 完成处),不要塞进 `EnemyManager`。 --- ## 9. 维护时的硬性不变量 - `CombatNodeComponent` 只做缓存和入口,不写战斗细节。 - `CombatScheduler` 是唯一 phase 状态机,不在别处推进 phase。 - `AliveEnemyCount` 必须由 tracked id 驱动,不回退到组名统计。 - 新战斗开始前必须清场。 - `ResetRuntime()` 必须清空地图相关运行态引用和 id。 --- ## 10. 快速阅读路径(新人 10 分钟) 1. `CombatNodeComponent.StartCombat()` 2. `CombatScheduler.Start() / OnUpdate() / BeginNextPhase()` 3. `EnemyManager.BeginPhase() / OnUpdate() / SpawnEnemies()` 4. `MapEntity.TryFindPathWorldPoints()` 5. `EnemyEntity.DespawnOnReachHouse()` 这 5 个点读完,基本能覆盖 90% 战斗节点行为。