7.6 KiB
7.6 KiB
敌人能力配置迁移到 AbilitySO —— 设计文档
- 日期:2026-07-21
- 范围:把 8 个「在能力 MonoBehaviour 上直接
[SerializeField]挂载」的能力的全部数值/资产参数(动画片段、时长、速度、次数、LayerMask 等)迁移到对应的EnemyAbilitySO子类 SO 中。场景对象组件引用保留在 MonoBehaviour。只改脚本 + 脚手架,不动现有 prefab / 资产(用户手动重生成替换)。
目标
- 能力的可配置参数统一放在 SO 里(数据与行为分离、跨实例复用、便于策划调参),消除「部分参数在 SO、部分在脚本」的分裂。
- 每个需要迁移的能力类型有一个专属
EnemyAbilitySO子类,类型安全、字段自解释、编辑器可见。 - 迁移边界清晰:数值/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 增加受保护助手:
/// <summary>把 _config 解析为期望的子类型;类型不符时显式报错(不回退)。</summary>
protected T ResolveConfig<T>() 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<T>),避免波及 attackSequence 型能力与 Unity 序列化面。基类保持非泛型,子类各自缓存类型化配置。
C. 每个能力 MonoBehaviour 的改动(以 ContactChase 为例)
private ContactChaseAbilitySO _cfg;
protected override void Awake()
{
base.Awake();
_cfg = ResolveConfig<ContactChaseAbilitySO>();
}
- 删除迁走的
[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<T>)。 - 修改:8 个能力 MonoBehaviour(删字段、Awake 解析、使用处改读 SO)。
- 可能修改:
CharacterWizardWindow.cs/DataHubWindow.cs(能力 SO 创建入口,按现状定)。 - 不改:
EnemyAbilitySO.cs(基类不动,除非需要把某几个当前 protected 成员对子类可见——实现时确认_config已 protected,子类无需访问基类私有)。
验证
- 编译门 0 错误。
- EditMode 测试:
ResolveConfig<T>类型匹配返回实例;类型不符/为 null 时报错(LogAssert.Expect(LogType.Error, ...)断言)。- 每个子类 SO 可
CreateInstance且暴露预期字段(反射校验字段名/类型存在)。
- MCP 放置验证:在活动场景放置一个能力对象、绑定对应子类 SO 实例,反射读取能力的
_cfg字段确认非空且参数值来自 SO(不必真跑 Animancer)。 - 自检工具:
Validate All ScriptableObjects无新增错误。
非目标
- 不迁移场景对象组件引用(
BodyContactDamage/HitBox/HurtBox)——保留在 MonoBehaviour 逐实例绑定。 - 不动
EnemyAttackSO/attackSequence(其clip已在 SO 层)。 - 不引入泛型能力基类。
- 不改现有 prefab / 资产(用户手动重生成)。