Files
zeling_v2/Docs_Dev/superpowers/specs/2026-07-30-boss-ai-single-track-design.md
T
joywayer b0fd040904 docs(enemy): 按 Task 1+2 审查结论修订 spec 与计划
spec:ResourceFull 由「无组件时恒 false」改为显式抛,与 Boss facet、AnchorAt
统一为「依赖组件缺失一律抛,不返默认值」。
计划:Task 16 补上 BossBase 残余三处行为树残留的清理项(含运行时告警字符串);
Task 19 记录 Patterns/ 是零外部引用的独立程序集,可整目录删。
2026-07-30 12:32:43 +08:00

570 lines
37 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.
# Boss AI 双轨统一:决策层与执行层一并归并到小怪轨
> 日期:2026-07-30  状态:设计已确认,待实施
> 背景:小怪走 `EnemyAiBrain`BrainGraph+ 组合式模块 + `EnemyAbilityBase`/`EnemyAbilitySO`
> Boss 走 `BossBase` + `BossSkillExecutor` + `BossSkillSO`。2026-07-27 的抽取决策记录把终态定为
> 「决策层统一 BrainGraph + 执行层按复杂度分层(Boss 保留 `BossSkillExecutor` 富编排)」。
> 本次经竞品调研与代码现状核查,**推翻其中"执行层分层"那一半**:Boss 侧那层富编排从未真正存在。
---
## 1. 结论摘要
| 决策 | 内容 |
|---|---|
| **决策层单轨** | Boss 走 BrainGraph。每个 Boss 一个 `AiScript` 定制类(已有的约 5% 定制路径),不建 Boss 骨架、不建 Boss 配方 |
| **执行层单轨** | Boss 招走 `EnemyAbilitySO` + `EnemyAttackSO` + `EnemyAbilityBase`。招式编排权威 = 动画时间轴(clip + 归一化时机 + 动画事件) |
| **Boss 专属不是"另一条轨"** | 而是旁挂 MonoBehaviour(浮空/击落/资源)+ 一个 `IBossControl` facet(阶段)|
| **阶段 = 换招池** | `BossPhaseAbilityGate` 按阶段启用/禁用能力组件。不在 SO 上加阶段字段 |
| **选招策略是可换的小构件** | `AttackSelectionMode` 加第三个值(加权随机 + 防重复)。不抽策略接口,enum 就是接缝 |
| **旧 Boss 轨整包退役** | 9 个脚本 + 5 个资产删除,判据是「有更优替代才删」 |
| **脚手架一并改** | `CharacterWizardWindow``SceneObjectPlacerTool.PlaceChaoFeng` 是旧轨资产的**创建链路**,不改则会持续产出旧轨对象(CLAUDE.md 第 2、6 条)|
核心原则:**决策归图,编排归动画,能不能用归招式自己,选哪个归选招器,演出归旁挂组件。**
---
## 2. 竞品证据
调研对象为同类型(横版平台动作 / Metroidvania)代表作及其续作的 Boss 设计,2026-07-30 完成。
以下六条是本设计的实证依据。
**2.1 技术轨本来就是单轨。** 代表作中所有敌人与 Boss 一律使用同一套可视化状态机(每个 Boss 一张
名为 `Control` 的图),与小怪用的是同一技术栈。差别只在状态数量,不在架构。
→ 双轨没有技术必要性。
**2.2 Boss 决策层极简,复杂度全在单招内部。** 招数量:某作最终 Boss 9 招,另几个知名 Boss 分别
6 招、4 招、3 招。决策形状统一是「选招 hub 态 → 加权随机挑一招(带防重复与位置门)→ 打完回 hub」。
→ Boss 决策层不需要比现有 `ApproachAttackEngagement` 复杂。
**2.3 阶段 = 换招池 + 一段过渡演出。** 某作最终 Boss 四阶段:P1 三招;血量阈值 1 **加**两招并提速;
阈值 2 **减掉**两招换成新招;P4 只剩两招。过渡是固定的「站定 → 仰头咆哮 → 硬直玩家」。
→ 阶段的本质是招池过滤器 + 一段无敌演出,现有 `BeginPhaseTransition` 已是正确形态。
**2.4 续作相对前作唯一的 Boss AI 改动,是选招谓词而非架构。** 开发者访谈明确说明:前作 Boss 是
反应式的——按玩家站位选招;续作去掉了这一点,改为主动式——按自身计划推进,不看玩家在哪。
**这条最有价值**:它说明「选招策略」应当是一个可替换的小构件,而不是一整套 AI 技术栈。
现有 `AttackSelectionMode` 只有 Priority / WeightedRandom 两维,缺的正是「读不读玩家位置」这一维。
**2.5 Boss 没有未发现层。** 竞品 Boss 不巡逻、不伪装、不警觉,靠竞技场进入 / 锁门 / 触发器开战。
→ 小怪骨架的第一层对 Boss 完全无用;Boss 要的是入场演出层。这是不复用 `PerceptionSkeleton` 的理由。
**2.6 committed 起手是共识。** 预警 → 起手 → 打完,不中途收招(某作的空中俯冲锁定的是**起手瞬间**的
玩家位置,而非实时追踪)。
→ 与 2026-07-29 组合式模块设计 §3.6 已定的「攻击起手后打完」一致,两轨在这点本来就同构。
---
## 3. 代码现状核查
### 3.1 Boss 现在没有决策层
`UseBossSkillWeighted` **零调用者**(原行为树任务已移除)。`ENM_ChaoFeng.prefab` 上**没有**
`EnemyAiBrain`(无 `_recipe` / `_definitionId` 序列化字段)。**嘲风目前打不起来。**
这条推翻了 2026-07-27 决策记录 §5 的执行建议「不要拿已能跑的 ChaoFeng 开刀验证」——
它不能跑,因此没有回归风险,正是首个验证对象。
### 3.2 Boss 的"富执行层"是空架构
- 全项目 **0 个** `AttackPatternSO` 资产、**0 个** `SkillSequenceSO` 资产。
- 5 个 `ABL_ChaoFeng_*.asset`(实为 `BossSkillSO`)除 `skillId``weight` 外全空:
`attackPatterns: []``sequenceOnMiss: {fileID: 0}``skillAnimation._Clip: {fileID: 0}`
`vulnerabilityWindows: []``counterResponses: []``cooldown: 0`
- 因此 `BossSkillExecutor.ExecuteSkillCoroutine` 跑一遍是纯空转:无动画、无序列、无判定。
### 3.3 零运行时消费者清单
| 成员 | 状态 |
|---|---|
| `BossSkillSO.attackPatterns` | 仅被两个编辑器窗口读取,运行时零读取 |
| `interactionTags` / `buildsRage` / `poiseWindow`BossSkillSO 上那份)| 零读取 |
| `PlayerCounterResponse.soulPowerGrant` / `spiritPowerGrant` | 零读取(`ExecuteCounterEffect` 未读)|
| `ArenaEventType/Params/Trigger/Data``IArenaInteractable` | 零消费者 |
| `TelegraphSystem` | 零消费者(调用方 `BD_TelegraphAttack` 已不存在)|
| `WeakPointSystem.GetDamageMultiplier()` | **零调用者**——注释称"由 BossStats 调用",项目无 `BossStats` 类型。弱点伤害倍率从未生效 |
| `BossSkillEventChannelSO` | `BossSkillExecutor` 在发,**全项目零订阅方** |
| `EnemyBase.ConsumeParryEvent` | 零调用者(`BD_OnParried` 已不存在)|
| `EnemyBase.ReceiveParry` | 零调用者(形态正确但未接线,见 §6.2)|
### 3.4 嘲风预制体实况
挂载的 Boss 组件只有三个:`BossSkillExecutor`5 个空技能)、`ChaoFengFloatController`
`ChaoFengKnockdownCounter`。**未挂** `WeakPointSystem``BossResource``TelegraphSystem``EnemyAiBrain`
而嘲风实际的招式编排走的是**动画事件**(clip 上的 `SpawnProjectile` 事件 → `ChaoFengBoss.SpawnProjectile(payload)`),
即小怪轨 `EnemyAttackSO` 的做法。**唯一的 Boss 用脚投票选了另一条路。**
### 3.5 现存弹反反制路径有 bug
`BossBase.HandleParrySuccess` 订阅的是 `ParryInfoEventChannelSO` 这个**全局**弹反成功频道,
无来源过滤。玩家弹反一只小怪也会让场上的 Boss 进入硬直。这是应当删除而非保留的另一个理由。
---
## 4. 架构:关注点归属
| 关注点 | 归属 | 依据 |
|---|---|---|
| 选状态 / 何时出手 / 何时换阶段 | AI 图(每 Boss 一个 `AiScript` | 这些是决策 |
| 一招怎么打(clip + 归一化时机 + HitBox + 弹体) | `EnemyAbilityBase` 子类 + `EnemyAbilitySO` | 与小怪同构 |
| 这招现在能不能用(阶段门) | 能力组件的启用状态(`BossPhaseAbilityGate`)| 阶段=换招池在数据上显式(§2.3)|
| 选哪一招(权重 / 防重复) | `EnemyAttackSelector` | 已是唯一选招器 |
| 阶段过渡无敌演出 | `BossBase.BeginPhaseTransition` | 已实现且正确,保留 |
| 死亡 | `Die` / `PerformDeath`(物理层)| 与小怪同一结论;Boss 已在走这条 |
| 浮空 / 击落计数 / 资源 | 旁挂 MonoBehaviourAI 图不知道其内部 | 单 Boss 或专项机制,不上升到骨架 |
### 4.1 为什么不建 Boss 骨架
竞品每个 Boss 都是一张手写图(§2.1)。Boss 数量在十几个量级,配方化省下的编译与复制成本极小,
而完全自由的价值很高。更关键的是:Boss 之间的差异远大于小怪(浮空 / 击落计数 / 多阶段竞技场 /
守卫箱),骨架会迅速退化成开关面板——正是 2026-07-29 刚从 `PerceptionStateMachine.Config` 上修掉的病。
复用 `PerceptionSkeleton` 也被否决:它专为感知升降级而存在,会给 Boss 挂上用不到的 `Alert` 态、
`InVision+HasAlert` 死边和 `leftVision → Rest` 脱战边(§2.5:Boss 不脱战)。
**统一的是运行时(`BrainGraph` + `AiRuntime` + `IsControllable` 让位门)与建态原语(`AiStateFragments`),
不是图的形状。**
### 4.2 共享图片段(不是可插拔模块)
Boss 图手写,但重复形状抽成静态片段——`AiStateFragments` 那个级别,不需要 `[SerializeReference]`
下拉、不需要 SO
```csharp
public static class BossFragments
{
/// 阶段过渡态:OnEnter 调 BeginPhaseTransition,过渡结束后转出。
public static BrainBuilder.StateBuilder PhaseTransition(
BrainBuilder b, string state, int targetPhase, float invincibleDuration);
/// 逼近 ↔ 选招攻击。转发到 ApproachAttackEngagement 提取出的静态建态方法,
/// 使交战模块与 Boss 图共用同一份实现(态名可指定,Boss 图可有多组)。
public static BrainBuilder.StateBuilder ApproachAttack(
BrainBuilder b, string approach, string attack, string rest);
/// 移动到第 index 个竞技场锚点(BossArenaAnchors),到位后转出。
public static BrainBuilder.StateBuilder MoveToAnchor(
BrainBuilder b, string state, int index);
}
```
`ApproachAttackEngagement.Build` 的建态逻辑提升为 `internal static`,模块与 `BossFragments` 各自转发。
入场直接用已有的 `AiStateFragments.Ability(b, Intro, introAbilityId)`
### 4.3 嘲风 AI 图形状(范例)
```csharp
[AiDefinition("ChaoFeng")]
public sealed class ChaoFengAi : AiScript
{
protected override void Build(BrainBuilder b)
{
b.Entry(Wait);
// 竞技场触发前静置(态名常量声明略)
AiStateFragments.Locomotion(b, Wait, LocomotionMode.Idle)
.To(Intro).OnEvent(AiSignal.Engaged);
// 入场演出
AiStateFragments.Ability(b, Intro, "chaofeng_intro")
.To(Ground).When(x => !x.Combat.IsAbilityRunning("chaofeng_intro"), "introDone");
// 阶段 0:地面,逼近 ↔ 选招(四招由阶段门放行)
BossFragments.ApproachAttack(b, Ground, GroundAttack, rest: Ground)
.To(PhaseTx).When(x => x.Vitals.HpBelow(0.5f), "hp<50%");
// 阶段过渡:无敌 + 浮空演出(ChaoFengBoss.OnBeginPhaseTransition 已实现)
BossFragments.PhaseTransition(b, PhaseTx, targetPhase: 1, invincibleDuration: 2f)
.To(Air).When(x => !x.Boss.IsPhaseTransitioning, "txDone");
// 阶段 1:空中,悬停 ↔ 风石(地面四招已被阶段门禁用)
AiStateFragments.Locomotion(b, Air, LocomotionMode.Face)
.To(AirAttack).When(x => x.Combat.HasEligibleAttack(), "attackInRange");
b.DeclareState(AirAttack)
.OnEnter(x => x.Combat.UseBestAttack())
.To(Air).When(x => !x.Combat.IsAbilityRunning(), "attackDone");
}
}
```
死亡不在图上:`ChaoFengBoss.Die()` 的击败演出协程照旧,`NotifyDecisionStop()`(原 `StopBehaviorTree`
`AiSignal.Died` 让全局死亡边把决策停住。与 2026-07-29 §3 修订的结论一致——**死亡不是一个决策**。
---
## 5. 新增的五件
| # | 内容 | 规模 |
|---|---|---|
| 1 | `IBossControl` facet + `IAiContext.Boss` | 小接口 |
| 2 | `BossPhaseAbilityGate` 组件 | 小组件 |
| 3 | `AttackSelectionMode.WeightedRandomAntiRepeat` + 选招器防重复 | 改两个文件 |
| 4 | `EnemyAnimationEvents` 补 4 个 case + 两个子系统引用字段 | 各 2–3 行 |
| 5 | `BossArenaAnchors` 组件 + 「移动到第 N 锚点」图片段 | 小组件 |
> 修订(2026-07-30,实施计划阶段):初稿列的第 5 件「`EnemyBase.SetInvincible(float)`」**已取消**。
> 核查 `PlayerAnimationEvents` 后确认玩家侧的 `EnableIFrame` / `DisableIFrame` 走的是
> `HurtBox.SetInvincible(bool)`——该 API **已存在**,无敌时长由动画时间轴上两个事件的间距表达。
> 敌人侧照同一形态接线即可,不需要在 `EnemyBase` 上新造带时长的无敌源。
> 这也更正确:时长归动画,不归代码常量。
### 5.1 `IBossControl`
```csharp
namespace BaseGames.AI
{
/// Boss 专属决策面。小怪图访问 IAiContext.Boss 会抛异常(显式报错,不静默兜底)。
public interface IBossControl
{
int CurrentPhase { get; }
bool IsPhaseTransitioning { get; }
bool ResourceFull { get; } // BossResource 满值;未挂该组件即抛(见下)
void BeginPhaseTransition(int targetPhase, float invincibleDuration);
// 竞技场锚点(§5.5)。归本 facet 而非黑板:见下方修订说明。
Vector2 AnchorAt(int index);
float DistanceToAnchor(int index);
}
}
```
> 修订(2026-07-30,实施计划阶段):锚点坐标初稿设计为经 `IAiContext.Blackboard` 传递
> `BossBase` 在 `Awake` 写入黑板键)。该方案有两处硬阻塞,已废弃:
> 1. **拿不到黑板** —— `EnemyAiBrain._context` 是私有字段且无公开访问器,`BossBase` 无从写入。
> 2. **时序坑** —— `EnemyBrainContext.ResetScratch()` 会 `_blackboard.Clear()`(对象池复用时调用),
> 写入点必须排在它之后,是个易错的隐式顺序依赖。
>
> 改为挂在 `IBossControl` 上:锚点本就是 Boss 专属知识,facet 已存在,`BossBase` 在 `Awake`
> 解析组件即可。净收益是少一个失败模式(黑板键缺失)、少一条隐式时序约束,且
> `DistanceToAnchor` 把"我离锚点多远"封在 Boss 内部,不需要给 AI 层引入新的位置概念。
`EnemyBrainContext.Boss => _boss ?? throw new InvalidOperationException(...)`
按 CLAUDE.md 第 6 条:小怪图误用它必须崩,不能返回一个空对象把问题盖住。
> 修订(2026-07-30Task 1+2 代码审查):`ResourceFull` 初稿定为「无资源组件时恒 false」,
> **已改为未挂 `BossResource` 时显式抛**。原设计是同一条第 6 条禁止的下游兜底——
> 图上挂了 `When(x => x.Boss.ResourceFull)` 边、预制体却漏挂组件时,那条边会永远不成立且零报错。
> 与紧邻的 `Boss` facet、以及 §5.5 的 `AnchorAt` 保持同一失败语义:**Boss 专属 facet 的每个成员,
> 在其依赖组件缺失时一律显式抛,不返回默认值。**
`BossBase` 实现 `IBossControl``EnemyBrainContext` 在构造时 `_boss = enemy as BossBase`
### 5.2 `BossPhaseAbilityGate`
```csharp
[Serializable] struct PhaseEntry { public EnemyAbilityBase ability; public int[] phases; }
```
`BossBase.EnterPhase(n)` 调用本组件按表启停能力组件;`OnSpawn` 重置到阶段 0 的启用集
(对象池复用)。同时 `EnemyAbilityBase.CanUse``&& enabled`——现状下禁用的组件
`CanUse` 仍为真,选招器会选中一个 `StartCoroutine` 必然失败的招。
**为什么阶段门不放 SO**:阶段可用性是跨所有 Boss 招的共性,而具体招式已经在用
`EnemyAbilitySO` 的类型化子类(`RushAbilitySO` / `RepeatSlamAbilitySO` …)。若做成
`BossAbilitySO : EnemyAbilitySO`,Boss 的近战招就要同时继承 Boss 与 Melee 两个子类——
C# 单继承直接堵死。
### 5.3 `WeightedRandomAntiRepeat`
选招器持有 `_lastPicked`,对上一招施加权重系数(默认 0.3,放 `EnemyStatsSO`)。
`BossBase.UseBossSkillWeighted` 的同名逻辑就此并入选招器后删除。
**为什么不抽 `IAttackSelectionStrategy`**:按 2026-07-27 §Layer B/C 的判据——三个 enum 值互不耦合、
`Select` 是一个三目、每模式一个 private 方法,不存在"每加一种就长一根 `if` 分支"的结构。
等真要做 §2.4 那种主动计划式选招时再抽,那时才有第 4、5 个消费者。**enum 就是留给它的接缝。**
### 5.4 动画事件补接(四个 case)
`AnimationEventType` 有 20 个值,`EnemyAnimationEvents.HandleEvent` 只接了 7 个
`EnableHitBox` / `DisableHitBox` / `SpawnProjectile` / `RoarStart` / `RoarEnd` /
`PhaseTwoStart` / `AnimationComplete`)。玩家侧 `PlayerAnimationEvents` 接得更全。
敌人侧漏掉的这四个恰好都是 Boss 复刻要用的:
| 补接 | 复刻的竞品能力 |
|---|---|
| `PlaySFX` / `TriggerFeedback` | **音效预警**——竞品有 Boss 用两句语音区分物理招与远程招,预警的是招式**类别**而非具体招式,让玩家缩小可能性但不能确定。这是最经济的 telegraph 手法,现在写不出来 |
| `EnableIFrame` / `DisableIFrame` | 招式中途无敌:突进穿过玩家时不吃伤害、传送消失期间无敌 |
四个 case 的实现与 `PlayerAnimationEvents` 逐字对称:反馈两件走 `IFeedbackPlayer.TriggerPreset` /
`PlaySFXById``EnemyFeedback` 已实现该接口),无敌两件走 `HurtBox.SetInvincible(bool)`API 已存在)。
`EnemyAnimationEvents` 因此需补两个子系统引用:`HurtBox _hurtBox``IFeedbackPlayer _feedback`
(后者在 `Awake``GetComponentInParent<IFeedbackPlayer>() ?? NullFeedbackPlayer.Instance` 解析,
与玩家侧同写法)。
### 5.5 `BossArenaAnchors`
竞技场锚点走位(站在悬空平台上出招、传送到房间固定角落)是竞品定点型 Boss 的核心。
`IEnemyLocomotion.MoveTo(Vector2)` 能到点,但锚点集合无处安放。
一个持有 `Transform[]` 的组件 + 一个「移动到第 N 个锚点」的图片段即可。
坐标经 `IBossControl.AnchorAt(int)` / `DistanceToAnchor(int)` 暴露给图(不走黑板,见 §5.1 修订)。
锚点物体放在场景里而非 SO 里——它随房间布局走。下标越界或引用为空**显式抛**,
不回退到 Boss 自身位置(那会让"锚点漏配"表现为"Boss 站着不动",是典型的下游兜底掩盖问题)。
---
## 6. 退役清单
判据:**有更优替代才删。** 按替代的成熟度分四类。
### 6.1 A 类:有现成更优替代 → 直接删
| 删除 | 更优替代(已存在) |
|---|---|
| `BossSkillSO` | `EnemyAbilitySO` + `EnemyAttackSO`(归一化时机、槽位 HitBox、`ProjectileConfigSO`|
| `AttackPatternSO` | `EnemyAttackSO``AoERadius/AoEOffset` 的更优形态是 HitBox 碰撞体几何;`KnockbackAngle``DamageSourceSO.KnockbackForce` + 命中方向 |
| `SkillSequenceSO` | `EnemyAbilitySO.attackSequence`(顺序连段)+ `RepeatSlamAbility`(范围内重复)|
| `BossSkillExecutor` | `EnemyAbilityBase` 子类 + `EnemyAttackSelector` |
| `InteractionTag` | `DamageFlags`——`CanBeParried``HurtBox.cs:77` 已生效,"不可弹反招"今天就能配 |
| `BossSkillSO.poiseWindow` | `EnemyAttackSO.hasPoiseWindow/poiseLevel/poiseStartT/poiseEndT` |
| `ArenaEventType/Params/Trigger/Data``IArenaInteractable` | `EnemySpawnerOnEvent`(动画事件生成 hazard)。`BossSkillEventChannelSO` 零订阅方,删执行器不丢现有功能;若将来场景机关要按招响应,接缝是在能力层发同一频道 |
| `BossSkillCategory` / `BossSkillType` | `AbilityCategory` |
| `TelegraphSystem` + `EnemyAbilitySO.telegraphVfxKey/telegraphDuration` + `EnemyAbilityBase.TelegraphRoutine` | clip 姿态 + 动画事件(§5.4 补接后)。三者是同一件事的三条平行路,且"生成 VFX 然后干等 N 秒"会在动画前插一段无动画静止 |
| `BossBase.UseBossSkill` / `UseBossSkillWeighted` / `LastUsedSkillId` / `IsSkillAvailableInPhase` / `CheckResourceCost` / 三个缓冲区 | `EnemyAttackSelector` + `WeightedRandomAntiRepeat` + `BossPhaseAbilityGate` |
| `BossResourceCost` / `autoTriggerOnFull` / `fullTriggerSkill` / `resetValueAfterTrigger` | AI 图条件边。"资源满自动放招"由 `BossResource` 直接调 `UseBossSkill` 是**绕过决策层**,违反"AI 只决策、能力负责实现";正确形态是图上一条 `When(x => x.Boss.ResourceFull)` 边进专属招态 |
| `EnemyBase.ConsumeParryEvent` + `_wasParried` / `_parryTimestamp` / `ParryEventTTL` | 零调用者;`ForceState(Stagger)` + `IsControllable` 让位门是更优表达 |
| `Editor/Modules/BossSkillModule.cs``Editor/Enemies/BossSkillSequenceWindow.cs` | 依附 `BossSkillSO`,类型删除后必然失效。`BossSkillModule` 实现 `IDataModule` 且无显式注册点(反射发现),删文件即可 |
| 5 个 `ABL_ChaoFeng_*.asset` | 空 `BossSkillSO`。注:`ABL_` 前缀本身**合规**`AssetFolderSpec.md` 第 298 行),删除原因是内容为空 |
### 6.2 B 类:替代需要顺手造,造完更优 → 删 + 造
**这三项现成的更优替代并不存在,是本设计建议顺手造一个。**
**`WeakPointSystem` → 倍率下沉到 `HurtBox`。**
`HurtBox` 没有伤害倍率字段,所以"打特定部位加伤"目前无载体。但 `WeakPointSystem` 的实现是坏的:
倍率写进自己的私有字段,`GetDamageMultiplier()` 零调用者,注释里的 `BossStats` 类型不存在。
更优形态是倍率归 `HurtBox` 自己(一个字段 + 伤害计算处一次乘法,约 3 行),弱点的定时开关由能力控制——
`MeleeVulnerabilityAbility` 已经证明「攻击后开放脆弱窗口」这个形态可行。
**`PlayerCounterResponse` / `CounterType` / `BossBase.ApplyCounterResponse` / `ExecuteCounterEffect` /
`CounterStaggerCoroutine` / `_onParrySuccess``EnemyBase.ReceiveParry`。**
`ReceiveParry(staggerDuration)` 已存在且形态正确(强制 Stagger + 打断全部能力 + 定时恢复,小怪 Boss
通用,不需要按 skillId 查表),但零调用者。删除现路径同时修掉 §3.5 的全局误伤 bug。
**代价须明示**`DamageInfo` 里**没有攻击者引用**(只有 `SourceProjectile`),所以"谁被弹反谁硬直"
这根线要在 Combat 层补——要么 `DamageInfo` 加 attacker,要么弹反成功时回调发起攻击的 `HitBox`
**列为独立后续任务,本次不做。** 期间 Boss 被弹反不会硬直,但优于现在的误伤行为。
**`BossSkillSO.designNote``EnemyAbilitySO.designNote`。**
无替代(`EnemyAttackSO.attackName` 只是调试名)。目标体量下 150+ 能力需要设计备注,加一个
`[TextArea]` 字段。
### 6.3 C 类:无替代 → 保留并改造
| 保留 | 改造 |
|---|---|
| `BossResource` + `BossResourceConfigSO` | 项目无第二套敌人资源条,"蓄能/怒气"是真机制。删掉自动触发三字段(见 A 类),保留数值维护 + `NormalizedValue`,新增 `IsFull`,触发权交回 AI 图(经 `IBossControl.ResourceFull`|
| `EnemyBase.ReceiveParry` | 形态正确,等 Combat 层接线(§6.2|
| `EnemyBase.StopBehaviorTree` | 改名 `NotifyDecisionStop()`(行为树债清理),同步改 `EnemyDeathSequence``ChaoFengBoss` 两处调用点 |
| `ChaoFengFloatController` / `ChaoFengKnockdownCounter` | 单 Boss 专属机制,形态正确,不动 |
| `BossBase.BeginPhaseTransition` / `EnterPhase` / `IsPhaseTransitioning` / `IsInvincible` 覆写 | 已实现且正确,仅新增 `IBossControl` 实现 |
### 6.4 D 类:会丢一个编辑器视图
删除 `BossSkillModule` / `BossSkillSequenceWindow` 后,**能力资产没有任何编辑器总览视图**
`EnemyModule` 不涉及能力资产,已核)。列为后续待补:`EnemyAbilityModule`(能力总览 +
射程 / 权重 / 冷却 / 阶段一览)。不阻塞本次——`AiDefinitionValidator` 那条校验线才是防
450+ 引用出错的关键,优先级更高。
### 6.5 明确不做的四件
| 不做 | 理由 |
|---|---|
| Boss 骨架 / Boss 配方 SO | §4.1 |
| `IAttackSelectionStrategy` 策略接口 | §5.3YAGNI,留 enum 接缝 |
| 主动计划式选招 | §2.4 是方向不是需求。等真要"不看玩家站位、按自身轮转"时加第 4 个 enum 值 |
| 多体 Boss 出招令牌 | 见 §7.2。等真设计双体 Boss 时再加 |
---
## 7. 表达力核对:能否复刻竞品 Boss
### 7.1 能直接表达(现有构件已覆盖)
| 竞品行为 | 项目对应构件 |
|---|---|
| 阶段加招 / **减招** | `BossPhaseAbilityGate` |
| 阶段过渡定格咆哮 + 硬直玩家 | `BeginPhaseTransition`(无敌)+ `RoarStart/RoarEnd` 动画事件(已接线)|
| 加权随机选招 + 防重复 | `EnemyAttackSelector` + `WeightedRandomAntiRepeat` |
| 按玩家距离 / 站位选招(反应式)| 招式自管圆形射程 `InAttackRange()` + `preferredMinRange/MaxRange` |
| 起手锁定玩家位置的落点攻击 | 嘲风 `tornado_large` 已实现(生成瞬间取玩家位置)|
| 不追踪的固定弧线弹幕(空间封锁)| `projectileCount` + `spreadAngleDeg` + `projectileFireT` |
| 突进穿越场地 | `RushAbility` / `ChargeAbility` / `MultiDashAbility` |
| 跳跃砸地 | `LeapAttackAbility` |
| 传送到玩家侧后突袭 | `BlinkStrikeAbility` |
| 天花板悬挂 / 掉落突袭 | `CeilingDropAbility` / `CeilingHangStrikeAbility` |
| 玩家还在范围内就反复砸 | `RepeatSlamAbility` |
| 召唤小怪 | `EnemySpawnerOnEvent`(动画事件驱动,零代码)|
| 招式霸体不被打断 | `interruptOnHurt=false` + `EnemyPoiseComponent` |
| 起手后打完不收招 | committed 攻击(`Attack` 态不挂脱战边)|
| **不可弹反招(必须闪避)** | 该招 `DamageSourceSO.Flags` 去掉 `CanBeParried``HurtBox.cs:77` 已在判定 |
| 入场演出 | `AppearAbility` + `AiSignal.Engaged`(新增该枚举值)|
| 击落 / 破防计数 | `ChaoFengKnockdownCounter` |
| 浮空 / 飞行阶段 | `FlyingNavigator` + `ChaoFengFloatController` |
最后那条顺带证实:`InteractionTag.Parryable` / `Unblockable` 与 Combat 层的 `DamageFlags`
**同一件事两处表达**,删掉不丢功能,反而消除打架隐患。
### 7.2 需要新组件的一处:多体 Boss 出招令牌
竞品有 Boss 在某阶段以两个实体同时行动且**交替出招**(一只出招时另一只等待,不会同时放同一招)。
项目 AI 图是每敌人独立,`Blackboard` 是 per-context`EnemyBrainContext``new Blackboard()`),
无跨敌人通道。
**这不是架构缺口**:形态是一个 `BossEncounterCoordinator` 持有出招令牌,参战 Boss 的选招多一个
「我持有令牌吗」的门。加一个组件 + `CanUse` 上一个门,不推翻本设计任何决策。
**现在不做**,等真设计双体 Boss 时再加。
### 7.3 两处看着像缺口、其实不是
**命中才追加连段。** `BossSkillSO.sequenceOnHit` 是整个 Boss SO 里唯一有真实语义却要被删的字段。
但竞品罕见(连段是固定的,不看是否命中;更常见的是"玩家还在范围内就重复",`RepeatSlamAbility` 已覆盖)。
真要的话形态是一个能力子类——`HitBox.OnHitConfirmed` 已存在,能力自己判断再决定跑不跑第二段。
不需要一整套序列 SO。
**竞技场机关联动。** `arenaEvents` / `IArenaInteractable` 只是一张零消费者的表。替代路径两条且都已存在:
招式动画上挂 `SpawnProjectile` 生成 hazard,或场景侧订阅招式事件频道。
---
## 8. 脚手架修改(CLAUDE.md 第 2、6 条)
**旧轨资产有两条创建链路,不改则会持续产出旧轨对象——这是根因,必须与代码一并修。**
### 8.1 `Editor/Character/CharacterWizardWindow.cs`
| 位置 | 现状 | 改为 |
|---|---|---|
| 第 8 行 | `using BaseGames.Boss;` | 删(仅为 `BossSkillSO`|
| 第 80 行 | `_bossId` 字段,注释 `kept for legacy SkillSequenceSO queries if any` | 删(死字段 + 死注释)|
| 第 692706 行 `CreateChaoFengSkillSO` | 建 `BossSkillSO`,设 `skillId` / `displayName` / `availablePhaseIndices` / `weight` | 建 `EnemyAbilitySO` 的**类型化子类**(按招式类型),设 `abilityId` / `weight` / `cooldown` / `category = AbilityCategory.Attack` / **`rangeRadius > 0`**。阶段索引不再进 SO,改由 `BossPhaseAbilityGate` 承载 |
| 第 709716 行 `ChaoFengSkillDefs` | `(name, id, phases, weight)` | `(name, id, soType, rangeRadius, cooldown, weight)`,与 `GetEnemyAbilityDefs` 的三元组形式对齐;阶段信息移到放置工具的阶段门配置 |
| 第 718727 行 `CreateAllChaoFengSOs` | 提示"检查 `BossSkillExecutor._skills` 绑定" | 改为"检查 `EnemyAiBrain._definitionId = ChaoFeng` 与各能力组件的 `_config` 绑定" |
| 第 785801 行 `BuildBossStatus` | `FindAtPath<BossSkillSO>` | `FindAtPath<EnemyAbilitySO>`;并新增一行 AI 图状态检查(`AiDefinitionRegistry` 是否含 `ChaoFeng`|
| 第 365 行 | `MakeJumpButton("Boss 技能序列查看器", BossSkillSequenceWindow.OpenWindow)` | 删(窗口已删)|
| 第 367 行 | `MakeJumpButton("Data HubBoss技能)", …)` | 改标签为 `Data Hub(敌人/能力)``BossSkillModule` 已删)|
| 第 348 行 | 放置说明提到 `BossSkillExecutor` 组件树 | 改为「`ChaoFengBoss` + 浮空控制器 + 击倒计数 + `EnemyAiBrain` + `BossPhaseAbilityGate` + 5 个能力组件 + HitBox × 4 + 炮口 × 3」|
**`rangeRadius > 0` 是硬要求**`EnemyAbilitySO.Validate()``EnemyBase.BuildAttackSelector()` 都会
`category == Attack && rangeRadius <= 0` 显式报错(招式永远够不着)。向导必须产出合规值,
不能留 0 让作者事后补——那是把创建链路的责任推给下游(第 6 条)。
### 8.2 `Editor/Scene/SceneObjectPlacerTool.cs``PlaceChaoFeng`,约 10891191 行)
| 位置 | 现状 | 改为 |
|---|---|---|
| 1089 | `GetOrAddComponent<BossSkillExecutor>(go)` | 删;改为挂 `EnemyAiBrain``_definitionId = "ChaoFeng"`+ `BossPhaseAbilityGate` + 5 个能力组件 |
| 1098 | 注释「技能执行时由 `BossSkillExecutor` 开关」 | 改为「由各能力组件按 `EnemyAttackSO.hitBoxSlot` 开关」|
| 11611172 | 收集 `BossSkillSO` 赋给 `skillExec._skills` | 改为把 `ABL_ChaoFeng_*``EnemyAbilitySO`)逐个赋给对应能力组件的 `_config`,并把 HitBox 绑进 `MeleeAttackAbility._hitBoxSlots` |
| 1169 | `未找到 BossSkillSO{n}` | `未找到 EnemyAbilitySO{n}` |
| 11861187 | 提示挂入 `BossSkillExecutor._hitBoxes` / 拖炮口进 `BossSkillSO` | 改为槽位绑定说明 + 炮口留在 `ChaoFengBoss` 上(现状已如此,`SpawnProjectile` 读的是 `ChaoFengBoss` 的 muzzle 字段)|
| 1190–1191 | 提示「技能执行仍由 `BossSkillExecutor` 负责」 | 删该括号说明;改为「Boss 走定制路径:`_definitionId = ChaoFeng`,不用配方」|
| 405407 | E004 的提示「★ 挂载 `BossSkillExecutor` 并指定 `BossSkillSO` 列表」 | 删(E004 归属已定为精英怪,走 `ApproachAttackEngagement`|
| 新增 | — | 一并放置 `BossFightTrigger`(发 `EVT_BossFightStarted` + `brain.Send(AiSignal.Engaged)`),否则 Boss 永远不开战(§3.1 缺口)|
### 8.3 顺带一致性修正
`EnemyAbilitySO``[CreateAssetMenu(fileName = "EAB_")]``AssetFolderSpec.md` 第 298 行的
`ABL_`(能力)及两个向导的实际命名都不一致 → 改为 `ABL_`
`EnemyAttackSO``EATK_` 规范未收录、无冲突,本次不动。
---
## 9. 文件清单
```
删除 Assets/_Game/Scripts/Enemies/Boss/BossSkillSO.cs
删除 Assets/_Game/Scripts/Enemies/Boss/AttackPatternSO.cs
删除 Assets/_Game/Scripts/Enemies/Boss/SkillSequenceSO.cs
删除 Assets/_Game/Scripts/Enemies/Boss/BossSkillExecutor.cs
删除 Assets/_Game/Scripts/Enemies/Boss/BossSkillTypes.cs (整个文件)
删除 Assets/_Game/Scripts/Enemies/Boss/WeakPointSystem.cs
删除 Assets/_Game/Scripts/Enemies/Boss/Patterns/TelegraphSystem.cs(连带空目录 Patterns/
删除 Assets/_Game/Scripts/Editor/Modules/BossSkillModule.cs
删除 Assets/_Game/Scripts/Editor/Enemies/BossSkillSequenceWindow.cs
删除 Assets/_Game/Data/Enemies/ChaoFeng/Abilities/ABL_ChaoFeng_{Boomerang,FanCombo,TornadoSmall,TornadoLarge,WindStone}.asset
新增 Assets/_Game/Scripts/AI/IBossControl.cs
新增 Assets/_Game/Scripts/Enemies/Boss/BossPhaseAbilityGate.cs
新增 Assets/_Game/Scripts/Enemies/Boss/BossArenaAnchors.cs
新增 Assets/_Game/Scripts/Enemies/Boss/BossFightTrigger.cs
新增 Assets/_Game/Scripts/Enemies/AIBrain/Modules/BossFragments.cs
新增 Assets/_Game/Scripts/Enemies/AIBrain/Ai/ChaoFengAi.cs
新增 Assets/_Game/Data/Enemies/ChaoFeng/Abilities/ABL_ChaoFeng_*.asset (经向导重建为 EnemyAbilitySO
改 Assets/_Game/Scripts/AI/IAiContext.cs + IBossControl Boss
改 Assets/_Game/Scripts/AI/AiSignal.cs + Engaged
改 Assets/_Game/Scripts/Enemies/AIBrain/EnemyBrainContext.cs 实现 Boss facet(非 Boss 抛异常)
改 Assets/_Game/Scripts/Enemies/Abilities/AttackSelectionMode.cs + WeightedRandomAntiRepeat
改 Assets/_Game/Scripts/Enemies/Abilities/EnemyAttackSelector.cs 防重复
改 Assets/_Game/Scripts/Enemies/Abilities/EnemyAbilityBase.cs CanUse += enabled;删 TelegraphRoutine
改 Assets/_Game/Scripts/Enemies/Abilities/EnemyAbilitySO.cs + designNote;− telegraph 两字段;fileName → ABL_
改 Assets/_Game/Scripts/Enemies/AIBrain/Modules/Engagement/ApproachAttackEngagement.cs 建态逻辑提静态
改 Assets/_Game/Scripts/Animation/EnemyAnimationEvents.cs + 4 个 case+ _hurtBox / _feedback 引用
改 Assets/_Game/Scripts/Enemies/EnemyBase.cs 改名 NotifyDecisionStop;− ConsumeParryEvent
改 Assets/_Game/Scripts/Enemies/Behaviors/EnemyDeathSequence.cs 调用点改名
改 Assets/_Game/Scripts/Combat/HurtBox.cs + 伤害倍率
改 Assets/_Game/Scripts/Enemies/Boss/BossBase.cs 瘦身约 150 行;实现 IBossControl
改 Assets/_Game/Scripts/Enemies/Boss/BossResource.cs 自动触发;+ IsFull
改 Assets/_Game/Scripts/Enemies/Boss/BossResourceConfigSO.cs 自动触发三字段
改 Assets/_Game/Scripts/Enemies/Boss/ChaoFengBoss.cs StopBehaviorTree() 调用点改为 NotifyDecisionStop()
改 Assets/_Game/Scripts/Editor/Character/CharacterWizardWindow.cs §8.1
改 Assets/_Game/Scripts/Editor/Scene/SceneObjectPlacerTool.cs §8.2
测试 Assets/Tests/EditMode/ 见 §11
文档 Docs/Guides/02_EnemyBoss 新增一节「Boss AI 怎么写」(定制路径 + 阶段门 + 图片段)
```
---
## 10. 嘲风落地路线
五招的归属:
| 招 | 能力类型 | 说明 |
|---|---|---|
| `fan_combo` | `MeleeAttackAbility` | 多段 `EATK_`HitBox 走槽位 |
| `boomerang` | `PlayClipAbility` | 生成逻辑已在 `ChaoFengBoss.SpawnProjectile`clip 上挂 `SpawnProjectile("boomerang")` 动画事件 |
| `tornado_small` | `PlayClipAbility` | 同上 |
| `tornado_large` | `PlayClipAbility` | 同上,落点锁定已实现 |
| `wind_stone` | `PlayClipAbility` | 同上,阶段门只在阶段 1 放行 |
阶段门配置:地面四招 `phases = [0]`,风石 `phases = [1]`
---
## 11. 测试策略
| 测试 | 覆盖 |
|---|---|
| `EnemyAttackSelectorTests`(扩)| `WeightedRandomAntiRepeat` 的防重复权重分布;上一招被禁用后不参与选招;无正权重时退化路径不变 |
| `BossPhaseAbilityGateTests`(新)| 阶段切换后启用集正确;`OnSpawn` 回阶段 0;未登记的能力不受影响 |
| `ChaoFengAiTests`(新)| fake `IAiContext` + fake `IBossControl` 驱动 `AiRuntime`,断言 `Wait → Intro → Ground ⇄ GroundAttack → PhaseTx → Air ⇄ AirAttack` 的状态序列与 trace label`Died` 全局边 |
| `EnemyBrainContextTests`(扩)| 非 Boss 敌人访问 `IAiContext.Boss``InvalidOperationException` |
| `HurtBoxTests`(扩)| 伤害倍率生效;默认 1 时行为不变 |
---
## 12. 风险与缓解
| 风险 | 缓解 |
|---|---|
| 删除面大,中途不可用 | **分两次提交**:先加(六件新增 + 嘲风能力资产 + AI 图跑通 + 两个脚手架改造),确认能打之后再删旧轨整包。任何时刻都有一条能跑的路 |
| 删脚本导致资产变 missing script | 删除顺序**必须**先资产后脚本 |
| 关闭 Domain Reload 下的陈旧态 | `BossPhaseAbilityGate` 的启用集是纯运行时态,`OnSpawn` 已重置,无静态态;`AiDefinitionRegistry` 的静态字典已有 `[RuntimeInitializeOnLoadMethod]` |
| 弹反反制暂时缺失 | §6.2 已列为独立后续任务。现状是"弹反任何敌人 Boss 都硬直"的错误行为,删掉是净改善 |
| 能力资产失去编辑器视图 | §6.4,后续补 `EnemyAbilityModule`;本次优先 `AiDefinitionValidator` |
| 脚手架改造遗漏导致旧轨复活 | 改完跑三件自检:`Validate All ScriptableObjects``Validate Address Keys``Physics2D Layer Matrix/Check`;并用向导重建嘲风 5 个资产验证产出合规 |
---
## 13. 注记
- 本设计推翻 2026-07-27 `shared-primitives-decision.md` §5 的「执行层按复杂度分层」,
保留其 §2`WeightedPick` 抽取)与 §3、§4(冷却 / HitBox 时序不抽)三条结论。
- 2026-07-29 `enemy-ai-composable-modules-design.md` §8 把「Boss 双轨统一」列为单独立项,
并注明「E004 归属未定前不动,`ApproachAttackEngagement` 按精英怪尺度做」。
本次确认 E004 归精英怪,`ApproachAttackEngagement` 的投入不作废,反而被 Boss 图复用(§4.2)。
- 竞品调研于 2026-07-30 完成,调研对象与来源链接见对应会话记录;本文档按 CLAUDE.md 第 4 条
只保留功能性描述,不写入具体作品与角色名。