geometry-tower-defense/docs/CombatNodeArchitecture.md

222 lines
6.3 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-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% 战斗节点行为。