diff --git a/Docs/superpowers/specs/2026-07-10-enemy-locomotion-refactor-design.md b/Docs/superpowers/specs/2026-07-10-enemy-locomotion-refactor-design.md new file mode 100644 index 00000000..ffc1df41 --- /dev/null +++ b/Docs/superpowers/specs/2026-07-10-enemy-locomotion-refactor-design.md @@ -0,0 +1,139 @@ +# 敌人执行层统一 + 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 完全无 AI;E004/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` FSM(Controlled/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 } + + // 对外只暴露"声明意图"的 API(AI/能力调用),执行在 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; } + } +} +``` + +- **内部后端不变**:地面怪用 `EnemyNavAgent`(PathBerserker2d)、飞行怪用 `FlyingDirectNavigator`,作为可替换的 `IPathAgent`;`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 机制。 +- **PlayMode(TestRoomA,E001)**:每期验 待机(伪装)→巡逻→警觉(朝向)→追击(接触伤害)→死亡 全链 + 脱离感知 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 明确覆盖飞行分支。 +- **物理原生易回归**: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 落地范式文档。