geometry-tower-defense/docs/CombatNodeArchitecture.md

6.3 KiB
Raw Blame History

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() 进入 RunningPhaseEnemyManager.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(可解析 floatDurationSeconds
  • EnemiesClearedIsPhaseSpawnCompleted && 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 动态阻挡/重算路径

  • 当前 SpawnEnemiesTryFindPathWorldPoints(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% 战斗节点行为。