Files
zeling_v2/Docs_Dev/superpowers/specs/2026-07-10-enemy-locomotion-refactor-design.md
T
2026-07-21 10:22:43 +08:00

144 lines
13 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.
# 敌人执行层统一 + AI/动画/属性瘦身 —— 设计文档
- 日期:2026-07-10
- 分支:`feat/braingraph-enemy-integration`
- 范围:**E001 样板 + 可复用框架**E002-E006/Boss 的 AI 落地范式在本文件定义、后续逐个实现。
- 关联:[[braingraph_ai_framework]]、[[ai_decision_only_delegates_abilities]]、[[enemy_nav_movement_architecture]]spec `2026-07-03-enemy-ai-framework-design.md`
## 1. 背景与问题
敌人 AI 已从三方 Behavior Designer 迁到自研 BrainGraph。四个子系统(movement / stats / state / AI+abilities)通篇审查后暴露三类问题:
1. **两套 AI 并存**~50 个 `BD_*` Behavior Designer 任务 + `BaseGames.Enemies.AI` asmdef + 三个 Opsive 包,运行期全死,只靠 `GRAPH_DESIGNER` 宏还在编译。→ **已在 P1 删除(本文件写作前完成)**
2. **"同一概念多实现"**
- **7 种巡逻**`PatrolAbility`(nav 随机) / `IMover.WalkRandom`(nav 随机, 仅测试) / `BD_WalkRandom` / `BD_InvestigateLastKnown` 子步 / `BD_Patrol`(速度踱步, 撞墙翻向) / `BD_PatrolWaypoints`(路点) / `FlyingDirectNavigator.WalkToRandom`(MovePosition)。既有 4 份"nav 随机游走"重复,又把有玩法差异的"踱步/路点"在迁移中弄丢。
- **4 套移动执行**`EnemyMovement` 直设 velocity / `PendingInput` 信号 / `FlyingEnemy` 直设 velocity / `FlyingDirectNavigator` MovePosition。nav 与 AI 会在同一 FixedUpdate 写同一批 `PendingInput` 字段互相覆盖。
- "朝向"4 入口、"停"4 入口、"设速度"跨 `EnemyStatsSO`/`TBM.movementSpeed`/`MoveInput` 三处。
- `IMover`(含 `UseChaseSpeed/UsePatrolSpeed/LookAround/WalkRandom`)零生产调用,被能力直调 `EnemyBase` 架空。
3. **冗余状态层**`AiPhase`(Idle/Patrol/Alert/Chase/Combat/Investigate/ReturnHome) 与 BrainGraph 状态严格 1:1,只用来选动画、AI 从不回读;`Combat/Investigate/ReturnHome` 是死枚举值。
4. **Model A 过度设计**`IdleAbility`/`PatrolAbility`/`AlertAbility``while(true) yield` 常驻协程,唯一作用是占住 `IsRunning``EnsureAbility` 空转——为琐碎行为各付出「协程+注册项+SO 资产」,且借用了本为攻击设计的 `EnemyAbilitySO``attackSequence/telegraph/range/LOS/priority` 全是死字段)。
5. **属性冗余**`EnemyStatsSO` 约 11/26 字段死或与 sensor 槽 / `DamageSourceSO` 重复;速度配在 3 处;玩家/敌人难度缩放逻辑重复。
**现状要点**:只有 **E001** 有可用 AI`[AiDefinition("E001")]` + `EnemyAiBrain`)。E002/E003/E006/Boss 完全无 AIE004/E005 prefab 里的 "BehaviorTree" 只是 `_stopBehaviorTree` bool 字段,非 Opsive 组件。→ 整套 AI/能力设计只在 1 个敌人上验证过,是重构的最佳时机。
## 2. 设计原则(不变)
- **AI 只决策,不 actuate**AI 决定"进哪个状态 / 转换条件 / 触发哪个能力 / 声明哪个移动意图";移动/朝向/停/速度/动画的**实现**不在 AI。见 [[ai_decision_only_delegates_abilities]]。
- **根因修复,不下游兜底**:删除多余抽象本身(而非留着不调用),让"AI 无法 actuate"在类型层成立。见 CLAUDE.md §6。
- **"能力"一词收回给战斗**:带冷却/预警/HitBox/接触伤害才叫 ability;纯移动/朝向归 locomotion。
## 3. 目标架构
三条正交轴,各自单一职责:
| 轴 | 组件 | 职责 | 谁改它 |
|---|---|---|---|
| 反应态 | `EnemyStateType` FSMControlled/Hurt/Stagger/KnockUp/Dead | 受击/硬直/死亡生命周期;当 AI 的门(`IsControllable`) | 伤害/招架/死亡 |
| 决策 | BrainGraph`AiRuntime`/`PerceptionStateMachine`/`AiScript`) | 感知→状态;声明移动意图 + 触发攻击能力 | 感知查询 |
| 执行 | **`EnemyLocomotion`(新)** + `EnemyAbilityBase`(攻击) | 移动/朝向/步态动画;攻击 | AI 声明意图;能力协程 |
### 3.1 核心组件 `EnemyLocomotion`(统一移动执行器)
单一移动入口,取代 4 套移动模型 + `IMover` + 三个协程能力 + `EnemyBase` 上散落的移动方法。
```csharp
namespace BaseGames.Enemies
{
public enum LocomotionMode { Idle, Patrol, Face, Approach }
public enum PatrolStrategy { Wander, Pace, Waypoints }
// 对外只暴露"声明意图"的 APIAI/能力调用),执行在 FixedUpdate 内部完成
public interface IEnemyLocomotion
{
void SetMode(LocomotionMode mode); // Idle(停)/ Patrol(按配置策略游走)
void Approach(Transform target); // 持续跟随(Chase 能力 / 调查用),派生 RunSpeed
void MoveTo(Vector2 point); // 一次性目标点
void Face(Vector2 lookAt); // 停 + 朝向(Alert 用)
void Stop();
LocomotionMode CurrentMode { get; }
bool IsMoving { get; }
}
}
```
- **内部双后端,作为可替换的 `IPathAgent`**
- **地面怪** = `EnemyNavAgent`(PathBerserker2d)。PB2d 是**表面图寻路**`NavSurface`/`NavSegment` + 表面间 `NavLink`),agent 始终映射到可行走表面——**不支持飞行/自由空间寻路**(已核实包源码:`NavSegmentPositionPointer`/`IsOnLink`/`KeepGrounded`,零 fly/aerial 支持)。
- **飞行怪** = `FlyingDirectNavigator`。**不走 PB2d**,直接 `Rigidbody2D.MovePosition` 向目标点**直线直飞**(含正弦悬停)。
- `EnemyLocomotion` 是二者之上的唯一门面,按敌人类型选后端。物理原生架构(寻路只给方向、velocity 执行、碰撞体底部对齐 y=0)保持,见 [[enemy_nav_movement_architecture]]。
- **巡逻 = `Patrol` 模式 + 每敌人序列化 `PatrolStrategy` + 参数**:一个组件三种玩法(Wander 随机 / Pace 撞墙翻向踱步 / Waypoints 路点序列),消灭 7 份实现并找回踱步/路点。
- **速度单一来源**:由模式派生(Patrol→`WalkSpeed`、Approach→`RunSpeed`,读 `EnemyStatsSO`)。删除 `EnemyMovement``EnemyStatsSO` 的二次直读接线(`EnemyMovement._config`),删除经 `TBM.movementSpeed` 的速度中转重复。
- **`Approach` 不改追击语义**`ContactChaseAbility` 保留为能力;仅把内部 `_enemy.MoveTo(player)` 换成 `Locomotion.Approach(player)`;接触伤害(战斗语义)仍在能力里。
### 3.2 AI 决策层用法(仍"只决策")
`PerceptionStateMachine` 状态不再绑"能力 id",改绑 **locomotion 意图 + 可选攻击能力 id**
```
Idle_Disguise : OnEnter → Locomotion.SetMode(Idle)
Move_Patrol : OnEnter → Locomotion.SetMode(Patrol)
Alert : OnEnter/Tick → Locomotion.Face(player)
Chase : OnEnter → Combat.UseAbility("e001_chase") // 能力内部 Approach(player)
Death : 终态(死亡演出走反应态 FSM)
```
- `SetMode/Face/Approach` 是**声明**(与原 `SetAiPhase` 同性质),执行在 `EnemyLocomotion`——AI 仍不碰 velocity/朝向实现。
- **删除** `IdleAbility`/`PatrolAbility`/`AlertAbility` 三协程能力、`ABL_E001_Idle`/`ABL_E001_Patrol`/`ABL_E001_Alert` 三 SO、`IMover` 接口及其在 `EnemyBrainContext` 的实现。
- `IAiContext` 增加 `IEnemyLocomotion Locomotion { get; }`(替换 `IMover Mover`)。`ICombatant.UseAbility/IsAbilityRunning/InterruptAbilities` 保留(攻击/Chase 用)。
- `PerceptionStateMachine.Config` 改为每状态填 `LocomotionIntent`(模式 + 可选 target 来源)+ 可选 `abilityId``AddAbilityState` 换成 `AddLocomotionState`
### 3.3 动画(删 `AiPhase`
- `EnemyLocomotion` 按当前模式/实际速度驱动步态 clip:静止→`Idle`、Patrol 移动→`Walk`、Approach→`Run`、Face→`Alert`。映射表 = 原 `AiPhase→AnimConfig` switch,改 **key 在 `LocomotionMode`** 上(放 `EnemyLocomotion` 或一个轻量 `EnemyAnimationDriver`)。
- 反应态(Hurt/Stagger/KnockUp/Dead)与攻击/出现能力**照旧各自播 clip**,覆盖在步态之上。
- **删除** `AiPhase` 枚举、`EnemyBase.SetAiPhase`/`CurrentAiPhase`/`OnAiPhaseChanged`,及所有能力里的 `SetAiPhase(...)``ReceiveAlert` 里用 `AiPhase` 做的"已交战不降级"判断改读 AI 当前状态名或一个 `bool IsEngaged`
### 3.4 属性清理(`EnemyStatsSO`
删除死/重复字段:`AttackDamage``AttackRange``DetectRange``DetectAngleDeg``EyeOffset``LOSBlockingMask`(感知全归 sensor 槽 `PhysicsPerceptionSystem`)、`AlertDuration``InvestigateDuration``KnockbackForce``HitStunDuration`(击退归 `DamageSourceSO`)、`HitTierConfig.heavyHitThreshold`(硬直由 Poise `Break` vs `PoiseLevel` 决定)。
- 速度单一来源(删 `EnemyMovement._config` 直读,改经 `EnemyLocomotion``Stats`)。
- (低优先,可选)玩家/敌人难度缩放抽共享 `ScalableVitals` helper(当前两边近乎逐行重复)。
## 4. E002-E006 / Boss 的 AI 落地范式(本文件定义,后续实现)
每个敌人 = **一个 `[AiDefinition("Exxx")]` `AiScript` 子类**,内容仅为:
1. 复用 `PerceptionStateMachine`(或 Boss 专用图),填状态名 + 每状态的 **locomotion 意图** + **攻击能力 id**
2. 攻击行为写成 `EnemyAbilityBase` 子类(已有一批:Melee/Projectile/Leap/Charge/CeilingDrop/…),配 `EnemyAbilitySO`(真攻击才需 cooldown/telegraph/HitBox)。
3. 巡逻/待机/警觉/追击**不写能力**,只在状态里声明 locomotion 意图(含选 `PatrolStrategy`)。
4. 脚手架 `SceneObjectPlacerTool.PlaceExxx``EnemyAiBrain(_definitionId)` + `EnemyLocomotion` + 攻击能力子节点。
近重复能力(后续合并,不阻塞本次):`CeilingDropAbility` vs `AnimatedCeilingDropAbility`(后者注释已声明取代前者);`AppearAbility` = `PlayClipAbility` + 一行 `SetAiPhase``FacePlayerAbility``AlertAbility` 的"朝向"原语;多个攻击能力里手抄的 HitBox 窗口 + `IsGrounded` 射线。
## 5. 分期实施(每期独立可提交、可回滚)
- **P1 死代码清除** — ✅ **已完成**commit `e894a9d`):删 `BD_*` + `BaseGames.Enemies.AI` asmdef + `GRAPH_DESIGNER` 宏 + 三个 `com.opsive.*` 包;恢复编译(0 错误)。
- **P2 引入 `EnemyLocomotion`** — 建组件 + 接入 Nav/Flying 后端;先与旧 API 并存,E001 切过去 PlayMode 跑通四态 + de-escalation。
- **P3 AI 改绑 locomotion 意图** — 删三协程能力/三 SO/`IMover``PerceptionStateMachine``AddLocomotionState``E001CaoZhiAi` 改声明意图;`ContactChaseAbility` 内部改 `Approach`。E001 验证。
- **P4 删 `AiPhase`** — 动画改由 locomotion 驱动;迁移 `SetAiPhase` 全部调用点;`ReceiveAlert` 改判据。
- **P5 Stats 清理** — 删死字段 + 速度单一来源(+ 可选难度缩放 helper)。
- **P6 巡逻策略补全** — 实现 `PatrolStrategy.Pace/Waypoints`(找回丢失玩法)。
- **文档产物** — 本文件即 E002-E06/Boss AI 落地范式;各敌人后续按范式逐个实现(独立任务)。
## 6. 测试
- **EditMode**BrainGraph 现有测试(`Assets/Tests/EditMode/AI/`)保留;`IMover` 相关用例改用 `IEnemyLocomotion` spy 或 `ICombatant` spy 验证状态机 Enter/Tick/Exit 机制。
- **PlayModeTestRoomAE001**:每期验 待机(伪装)→巡逻→警觉(朝向)→追击(接触伤害)→死亡 全链 + 脱离感知 de-escalation;断言 AI 脚本零 `velocity/朝向` 实现、动画随 locomotion 模式切换、console 0 报错。
- **自检**`Validate All ScriptableObjects` / `Validate Address Keys` / `Physics2D Layer Matrix Check`CLAUDE.md §3)。
## 7. 风险与缓解
- **飞行怪双模型**`FlyingEnemy`/`FlyingDirectNavigator``MovePosition`,需在 `EnemyLocomotion` 内以 `IPathAgent` 后端形式收编,避免又留一套并行执行。P2 明确覆盖飞行分支。`EnemyLocomotion` API 对上层一致,`SetMode/Approach/MoveTo` 在飞行后端映射为直飞目标点。
- **飞行绕障是已知缺口(非本次范围)**:PB2d 表面寻路给不了飞行绕障,现有 `FlyingDirectNavigator` 是**直线直飞、无障碍绕行**;当前 E002/E004/E005/E006 prefab 两种导航组件都未挂(飞行导航"有实现未接线")。真要做"能绕墙的飞行怪"时需**单独的飞行寻路方案**(如 2D 网格 A*/点图),不能靠 PB2d 或现有直飞。本次不实现;作为落地飞行怪 AI 时的前置决策记录在案。
- **物理原生易回归**nav 只给方向、velocity 执行、`TBM.SegmentMovement` 关、碰撞体底部 y=0——重构中必须保留,见 [[enemy_nav_movement_architecture]]。
- **动画覆盖顺序**:反应态/攻击 clip 必须能压过步态 clip;P4 需验证受击/攻击时步态不抢播。
- **`AiPhase` 删除波及面**`SetAiPhase` 有多处调用点(能力、`ReceiveAlert`、gizmo/overlay);P4 逐点迁移并保留调试可视化(改读状态名)。
- **范围克制**:本次只在 E001 落地并验证;E002-E006/Boss 仅定范式,避免一次性大改多敌人放大回归面。
## 8. 成功判据
- 编译 0 错误;E001 PlayMode 行为与重构前一致(四态 + de-escalation + 接触伤害)。
- 代码库中"巡逻"实现 1 个(`EnemyLocomotion` + 策略)、"移动执行"入口 1 个、无 `AiPhase`、无 `IMover`、无 Idle/Patrol/Alert 协程能力与其 SO。
- `EnemyStatsSO` 无死字段;速度单一来源。
- E002-E006/Boss 有明确、可照抄的 AI 落地范式文档。