# 敌人能力配置迁移到 AbilitySO —— 设计文档 - 日期:2026-07-21 - 范围:把 8 个「在能力 MonoBehaviour 上直接 `[SerializeField]` 挂载」的能力的**全部数值/资产参数**(动画片段、时长、速度、次数、LayerMask 等)迁移到对应的 `EnemyAbilitySO` **子类** SO 中。场景对象组件引用保留在 MonoBehaviour。只改脚本 + 脚手架,不动现有 prefab / 资产(用户手动重生成替换)。 ## 目标 1. 能力的可配置参数统一放在 SO 里(数据与行为分离、跨实例复用、便于策划调参),消除「部分参数在 SO、部分在脚本」的分裂。 2. 每个需要迁移的能力类型有一个专属 `EnemyAbilitySO` 子类,**类型安全**、字段自解释、编辑器可见。 3. 迁移边界清晰:**数值/LayerMask/Clip/资产 SO → 子类 SO**;**场景对象组件引用(`BodyContactDamage`/`HitBox`/`HurtBox`)→ 保留在 MonoBehaviour**(逐实例场景引用无法进共享 SO 资产)。 ## 背景(现状) - `EnemyAbilitySO`(`Assets/_Game/Scripts/Enemies/Abilities/EnemyAbilitySO.cs`)是能力配置基类,承载通用配置:`abilityId`、`attackSequence`、`cooldown`、telegraph、中断规则、调度提示、`exclusionGroup`、`priority`。 - `EnemyAbilityBase`(MonoBehaviour 抽象基类)持有 `[SerializeField] protected EnemyAbilitySO _config`,生命周期/冷却/中断在基类。 - `EnemyAttackSO.clip`(`ClipTransition`)已在 SO 层,供 attackSequence 型能力(Melee/Charge/Blink/Projectile/Leap/ChaoFeng 等)使用——**这类能力已是 SO 驱动,不在本次范围**。 - 有 8 个能力在 MonoBehaviour 上直接挂参数(含 `ClipTransition` 及其它数值),是本次迁移对象。 - 现有 17 个 `ABL_*.asset` 配置资产均为基类 `EnemyAbilitySO` 类型;片段目前挂在各 prefab 的能力组件实例上。 ## 设计 ### A. 八个子类 SO 在新子目录 `Assets/_Game/Scripts/Enemies/Abilities/Configs/` 下,为每个能力建一个 `XxxAbilitySO : EnemyAbilitySO`,各带 `[CreateAssetMenu(menuName = "BaseGames/Enemies/Abilities/<名>", fileName = "ABL_")]`。字段迁移如下(**以脚本实际字段为准,实现时逐文件核对**;下表为已核实的字段): | 能力 MonoBehaviour | 子类 SO | → 迁入 SO 的字段 | 保留在 MonoBehaviour(场景引用) | |---|---|---|---| | `ContactChaseAbility` | `ContactChaseAbilitySO` | `startClip, loopClip, endClip`(ClipTransition);`dashSpeed`(float) | `BodyContactDamage _contactDamage` | | `RepeatSlamAbility` | `RepeatSlamAbilitySO` | `startClip, loopClip, endClip`;`hitActiveTime, staggerDuration`(float);`slamCount`(int) | `HitBox _hitBox` | | `CeilingHangStrikeAbility` | `CeilingHangStrikeAbilitySO` | `strikeClip, loopClip, endClip`;`hangDuration`(float) | `HitBox _attackHitBox`, `HurtBox _hurtBox` | | `AnimatedCeilingDropAbility` | `AnimatedCeilingDropAbilitySO` | `fallLoopClip`;`fallGravityScale, maxFallTime, recoveryTime`(float);`groundMask`(LayerMask) | `BodyContactDamage _contactDamage` | | `AppearAbility` | `AppearAbilitySO` | `appearClip` | — | | `FacePlayerAbility` | `FacePlayerAbilitySO` | `faceClip` | — | | `MeleeVulnerabilityAbility` | `MeleeVulnerabilityAbilitySO` | `attackClip`;打击归一化时机字段;`staggerDuration`(float) | `HitBox _hitBox`, `HurtBox _hurtBox` | | `PlayClipAbility` | `PlayClipAbilitySO` | `clip` | — | - 字段名去掉下划线前缀(SO 公有字段风格,对齐 `EnemyAbilitySO`/`EnemyAttackSO` 现有公有字段),保留原 `[Header]`/`[Tooltip]`/`[Min]`/`[Range]` 语义。 - 子类只加自己的字段,通用配置继续由基类承载。 ### B. 基类:类型化配置解析(根因显式报错,无兜底) 在 `EnemyAbilityBase` 增加受保护助手: ```csharp /// 把 _config 解析为期望的子类型;类型不符时显式报错(不回退)。 protected T ResolveConfig() where T : EnemyAbilitySO { if (_config is T typed) return typed; Debug.LogError($"[{GetType().Name}] _config 需为 {typeof(T).Name}," + $"实际 = {(_config != null ? _config.GetType().Name : "null")}。请重建为正确的子类 SO。", this); return null; } ``` - 类型不符或漏配时**显式报错**(符合 CLAUDE.md §6 根因修复、禁止下游兜底),不静默回退到默认值。 - 不引入泛型基类(`EnemyAbilityBase`),避免波及 attackSequence 型能力与 Unity 序列化面。基类保持非泛型,子类各自缓存类型化配置。 ### C. 每个能力 MonoBehaviour 的改动(以 ContactChase 为例) ```csharp private ContactChaseAbilitySO _cfg; protected override void Awake() { base.Awake(); _cfg = ResolveConfig(); } ``` - 删除迁走的 `[SerializeField]` 字段(`_startClip/_loopClip/_endClip/_dashSpeed`)。 - 使用处:`_startClip` → `_cfg.startClip`,`_dashSpeed` → `_cfg.dashSpeed`,其余同理。空片段判空逻辑不变(`_cfg.startClip.Clip != null`)。 - 若 `_cfg == null`(漏配/类型错),执行路径应安全短路(`ExecuteCoroutine` 起始判 `_cfg == null` 则 `yield break`),错误已由 `ResolveConfig` 报出——不静默补默认值。 - 场景引用字段(`_contactDamage` 等)保留不动。 ### D. 脚手架 / 创建入口 - 8 个子类各自 `[CreateAssetMenu]`:用户可从菜单创建正确类型的 `ABL_` 资产。 - 若 `CharacterWizardWindow` / `DataHubWindow` 有创建能力 SO 的入口,改为按能力类型创建对应子类(实现前先核对这两个入口现状;无则仅靠 CreateAssetMenu)。 - `SceneObjectPlacerTool` 中 `AssignAsset(ability, "_config", …, "ABL_xxx")` 按资产名引用——用户重建的 `ABL_` 资产为新子类即可正确绑定,脚手架代码基本不变(如需,补充「资产类型应为子类」的自检提示)。 ### E. 现有资产迁移 - 按用户选择:**代码 + 脚手架保证正确,用户手动重建**这 8 类相关的 `ABL_` 资产(旧资产是基类 `EnemyAbilitySO`,重建为子类并把参数填进 SO),并重生成对应 prefab。 - attackSequence 型能力(ChaoFeng/Melee/Charge/Blink/Projectile/Leap 等)继续用基类 `EnemyAbilitySO`,不受影响。 ## 影响文件 - 新增:`Assets/_Game/Scripts/Enemies/Abilities/Configs/` 下 8 个子类 SO 脚本。 - 修改:`EnemyAbilityBase.cs`(加 `ResolveConfig`)。 - 修改:8 个能力 MonoBehaviour(删字段、Awake 解析、使用处改读 SO)。 - 可能修改:`CharacterWizardWindow.cs` / `DataHubWindow.cs`(能力 SO 创建入口,按现状定)。 - 不改:`EnemyAbilitySO.cs`(基类不动,除非需要把某几个当前 protected 成员对子类可见——实现时确认 `_config` 已 protected,子类无需访问基类私有)。 ## 验证 - 编译门 0 错误。 - EditMode 测试: - `ResolveConfig` 类型匹配返回实例;类型不符/为 null 时报错(`LogAssert.Expect(LogType.Error, ...)` 断言)。 - 每个子类 SO 可 `CreateInstance` 且暴露预期字段(反射校验字段名/类型存在)。 - MCP 放置验证:在活动场景放置一个能力对象、绑定对应子类 SO 实例,反射读取能力的 `_cfg` 字段确认非空且参数值来自 SO(不必真跑 Animancer)。 - 自检工具:`Validate All ScriptableObjects` 无新增错误。 ## 非目标 - 不迁移场景对象组件引用(`BodyContactDamage`/`HitBox`/`HurtBox`)——保留在 MonoBehaviour 逐实例绑定。 - 不动 `EnemyAttackSO` / `attackSequence`(其 `clip` 已在 SO 层)。 - 不引入泛型能力基类。 - 不改现有 prefab / 资产(用户手动重生成)。