# 敌人执行层统一 + 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; } } } ``` - **内部双后端,作为可替换的 `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 机制。 - **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 明确覆盖飞行分支。`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 落地范式文档。