40 KiB
小怪与 Boss Unity 配置手册
文件位置:
Docs/Guides/02_Enemy_Boss_Setup_Guide.md
版本:1.0 · 适用对象:策划 / 技术美术
对应实现计划:Docs/Plan/小怪与Boss实现计划-01.md
本文档面向在 Unity Editor 中负责创建 Prefab、配置 ScriptableObject、制作动画 Clip、搭建行为树的策划开发者。所有 C# 脚本均已由程序完成,本文档只涉及 Editor 操作。
目录
- 必读:核心规则(违反会导致运行错误)
- 前置任务:全局配置
- 通用操作流程
- E001 草蛭
- E002 簧蛭
- E003 幼蛭
- E004 蛭母(小Boss)
- E005 肥蛭(精英怪)
- E006 讙
- Boss 嘲风
- Addressables 注册步骤
- 常见错误排查
1. 必读:核心规则
⚠️ 在动手之前请完整阅读本节。以下规则违反后不会有编译错误,但运行时动画/AI 会失效。
规则 A:动画永远属于能力脚本,不属于行为树
| 正确做法 | 错误做法 |
|---|---|
| 技能动画在对应 Ability 组件的字段上绑定 ClipTransition | 在行为树中使用 BD_PlayAnimation 节点播放技能动画 |
框架状态动画(Idle/Walk/Run/Dead)在 EnemyAnimationConfigSO 绑定 |
在行为树中手动切换 Idle、Walk、Run |
框架自动负责的动画(不需要任何操作):
| 动画 | 自动触发条件 |
|---|---|
| Idle | EnemyBase 初始化时;BD_SetAiPhase(Idle) 时 |
| Walk | BD_SetAiPhase(Patrol) 时 |
| Run | BD_SetAiPhase(Chase) 时;BD_ChasePlayer 时 |
| Dead | HP 归零后 Die() 自动触发 |
结论:行为树只做条件判断(BD_IsXxx)和行为触发(BD_UseAbility、BD_SetAiPhase、BD_MoveToPlayer)。
规则 B:能力组件必须挂在 Prefab 子节点 Abilities/ 下
EnemyAbilityRegistry 在游戏开始时自动扫描根节点的所有子 GameObject,收集所有 EnemyAbilityBase 子类组件。
能力组件挂在根节点本身无法被注册,必须放在子层级(推荐统一放 Abilities/ 子节点)。
规则 C:abilityId 全局唯一
每个 EnemyAbilitySO 的 abilityId 字段在整个项目中不能重复。推荐命名格式:
{敌人ID}_{技能描述} — 例:e001_activate、e004_headslam、e006_leap
规则 D:命名规范(必须严格遵守)
| 资产类型 | 命名格式 | 示例 |
|---|---|---|
| 敌人 StatsSO | ENM_{EnemyID}_Stats.asset |
ENM_E001_Stats.asset |
| 能力 AbilitySO | ABL_{EnemyID}_{Name}.asset |
ABL_E001_Activate.asset |
| 敌人 Prefab | ENM_{Name}.prefab |
ENM_CaoZhi.prefab |
| 弹体 Prefab | PROJ_{Name}.prefab |
PROJ_Boomerang.prefab |
| 动画 Clip | {Action}.anim(无前缀) |
Idle.anim、Skill_Start.anim |
| 精灵图 | {ID}_{Name}_{Action}.png |
E001_CaoZhi_Idle.png |
2. 前置任务:全局配置
在创建任何敌人之前,确认以下全局设置已完成。
2-A:Layer 确认
打开 Edit → Project Settings → Tags and Layers,确认以下 Layer 存在:
| Layer 名 | 用途 |
|---|---|
EnemyHitBox |
敌人攻击碰撞盒(HitBox 组件) |
EnemyHurtBox |
敌人受击碰撞盒(HurtBox 组件) |
EnemyProjectile |
敌人弹体 |
PlayerHitBox |
玩家攻击碰撞盒 |
2-B:对象池 PoolKey 注册
以下弹体/小怪 Prefab 需要注册到对象池(在对象池管理 SO 中添加条目)。Pool Key 必须与 Addressable Address 完全一致:
| Pool Key | Prefab | 说明 |
|---|---|---|
ENM_YouZhi |
ENM_YouZhi.prefab |
E005 死亡时生成 E003 |
PROJ_ZhiMu_Acid |
PROJ_ZhiMu_Acid.prefab |
E004 酸液弹 |
PROJ_FeiZhi_Acid |
PROJ_FeiZhi_Acid.prefab |
E005 酸液弹 |
PROJ_Boomerang |
PROJ_Boomerang.prefab |
嘲风回旋扇 |
PROJ_TornadoSmall |
PROJ_TornadoSmall.prefab |
嘲风小龙卷 |
PROJ_TornadoLarge |
PROJ_TornadoLarge.prefab |
嘲风大龙卷 |
PROJ_WindStone |
PROJ_WindStone.prefab |
嘲风风石 |
3. 通用操作流程
每个敌人的创建流程分为以下 5 步,后续章节按此模板展开:
Step 1 制作动画 Clip
Step 2 创建 ScriptableObject(StatsSO + AbilitySO)
Step 3 搭建 Prefab 结构并配置组件
Step 4 搭建行为树
Step 5 注册 Addressables
如何创建 EnemyStatsSO
右键 Assets/_Game/Data/Enemies/{EnemyID}/ → Create → BaseGames/Enemies/Enemy Stats → 命名 ENM_{EnemyID}_Stats.asset
如何创建 EnemyAbilitySO
右键 Assets/_Game/Data/Enemies/{EnemyID}/ → Create → BaseGames/Enemies/Enemy Ability → 命名 ABL_{EnemyID}_{Name}.asset
EnemyAnimationConfigSO 标准字段映射
| AnimConfig 字段 | 绑定的 Clip | 框架触发时机 |
|---|---|---|
Idle |
待机/伪装 Clip | 游戏开始时;SetAiPhase(Idle) |
Walk |
巡逻移动 Clip | SetAiPhase(Patrol) |
Run |
追击/快速移动 Clip | SetAiPhase(Chase) |
Turn |
转身 Clip | EnemyMovement 检测到方向变化(需开启 _enableTurnAnimation) |
Dead |
死亡 Clip | Die() |
Hurt |
受击 Clip | 框架受击状态 |
Stagger |
硬直 Clip | 框架硬直状态 |
⚠️ 技能动画(如
Skill_Start、Skill_Loop)不放在 AnimConfig,而是直接绑定到各 Ability 组件的ClipTransition字段。
4. E001 草蛭
设计要点:默认伪装为环境装饰,感知玩家后激活,进入 Skill_Start→Skill_Loop 追击模式。
Step 1:制作动画 Clip
路径:Assets/_Game/Art/Characters/Enemies/E001/Animations/
| Clip 文件名 | 用途 | 绑定位置 |
|---|---|---|
Idle.anim |
伪装待机(循环) | AnimConfig.Idle |
Move.anim |
巡逻爬行(循环) | AnimConfig.Walk |
Flip.anim |
转身(单次) | AnimConfig.Turn |
Skill_Start.anim |
激活前摇(单次) | PlayClipAbility._clip |
Skill_Loop.anim |
追击循环(循环) | ContactChaseAbility._loopClip |
Skill_End.anim |
追击收招(单次) | ContactChaseAbility._endClip |
Death.anim |
死亡(单次) | AnimConfig.Dead |
Step 2:创建 ScriptableObject
文件路径:Assets/_Game/Data/Enemies/E001/
ENM_E001_Stats.asset(EnemyStatsSO):
| 字段 | 说明 |
|---|---|
| MaxHP | 策划填写 |
| WalkSpeed | 巡逻速度 |
| RunSpeed | 追击速度 |
| DetectRange | 感知范围(与 aggro 传感器半径一致) |
ABL_E001_Activate.asset(EnemyAbilitySO):
| 字段 | 值 |
|---|---|
| abilityId | "e001_activate" |
| cooldown | 0 |
| interruptOnHurt | true |
ABL_E001_Chase.asset(EnemyAbilitySO):
| 字段 | 值 |
|---|---|
| abilityId | "e001_chase" |
| cooldown | 0 |
| preferredMaxRange | 与 aggro 传感器半径一致 |
Step 3:搭建 Prefab
路径:Assets/_Game/Prefabs/Enemies/E001/ENM_CaoZhi.prefab
Prefab 层级结构:
ENM_CaoZhi (根节点)
├── 组件:EnemyBase、EnemyMovement、EnemyNavAgent、NavAgent、TransformBasedMovement
├── 组件:EnemySensorHub
├── 组件:Rigidbody2D(Dynamic)、Collider2D(主碰撞体)
├── HurtBox/
│ ├── 组件:HurtBox、Collider2D(Is Trigger=true, Layer=EnemyHurtBox)
├── ContactDamageZone/
│ ├── 组件:BodyContactDamage、HitBox、Collider2D(Is Trigger=true, Layer=EnemyHitBox)
└── Abilities/
├── 子节点 Activate_Ability → 组件:PlayClipAbility
└── 子节点 Chase_Ability → 组件:ContactChaseAbility
组件配置详情:
EnemyBase:
_stats:拖入ENM_E001_Stats.asset_animConfig:拖入对应EnemyAnimationConfigSO(需先创建,绑定 Idle/Walk/Run/Turn/Dead Clip)
EnemyMovement:
_enableTurnAnimation:勾选 ✓(使用 Flip 转身动画)
EnemySensorHub(在 Inspector 中添加 _slots):
| slotName | Sensor 类型 | 说明 |
|---|---|---|
aggro |
RangeSensor2D | 感知半径 = DetectRange |
wall_ahead |
RaySensor2D | 向前射线,检测墙壁 |
ledge |
RaySensor2D | 向前下方射线,检测悬崖 |
PlayClipAbility(挂在 Activate_Ability 子节点):
_config:拖入ABL_E001_Activate.asset_clip:拖入Skill_Start.anim对应的 ClipTransition
ContactChaseAbility(挂在 Chase_Ability 子节点):
_config:拖入ABL_E001_Chase.asset_loopClip:Skill_Loop.anim_endClip:Skill_End.anim_contactDamage:拖入ContactDamageZone/节点上的BodyContactDamage组件_sensorHub:拖入根节点上的EnemySensorHub组件_aggroSlotName:"aggro"
Step 4:搭建行为树
在根节点挂载 BehaviorTree 组件,创建如下结构:
Selector
├── Sequence [死亡]
│ ├── BD_IsStateMatch → State: Dead
│ └── BD_StopMovement
│
├── Sequence [激活]
│ ├── Selector
│ │ ├── BD_IsAiPhase → Phase: Idle
│ │ └── BD_IsAiPhase → Phase: Patrol
│ ├── BD_IsSensorDetecting → SlotName: "aggro"
│ ├── BD_UseAbility → AbilitySO: ABL_E001_Activate
│ └── BD_SetAiPhase → Phase: Chase
│
├── Sequence [追击]
│ ├── BD_IsAiPhase → Phase: Chase
│ └── BD_UseAbility → AbilitySO: ABL_E001_Chase
│
└── Selector [巡逻]
├── Sequence [待机]
│ ├── BD_IsAiPhase → Phase: Idle
│ └── BD_WaitRandom → Min/Max: 填写待机时长范围
└── Sequence [移动]
├── BD_IsAiPhase → Phase: Patrol
└── BD_Patrol
⚠️ 最外层巡逻节点必须是 Selector 而不是 Sequence。否则待机结束后不会进入巡逻分支。
Step 5:注册 Addressables
- Address:
ENM_CaoZhi - Group:
Enemies - Labels:
Enemy
5. E002 簧蛭
设计要点:固定位置(悬挂天花板),无移动能力。Skill_Loop 期间为脆弱窗口(HurtBox 开启)。
Step 1:动画 Clip
路径:Assets/_Game/Art/Characters/Enemies/E002/Animations/
| Clip 文件名 | 用途 | 绑定位置 |
|---|---|---|
Idle.anim |
待机(循环) | AnimConfig.Idle |
Skill_Strike.anim |
出击(单次) | CeilingHangStrikeAbility._strikeClip |
Skill_Loop.anim |
脆弱悬挂(循环) | CeilingHangStrikeAbility._loopClip |
Skill_End.anim |
收回(单次) | CeilingHangStrikeAbility._endClip |
Death.anim |
死亡 | AnimConfig.Dead |
Step 2:ScriptableObject
路径:Assets/_Game/Data/Enemies/E002/
ENM_E002_Stats.asset:WalkSpeed/RunSpeed=0(固定单位)。
ABL_E002_CeilingStrike.asset:
| 字段 | 值 |
|---|---|
| abilityId | "e002_ceiling_strike" |
| cooldown | 悬挂时长+恢复时长(策划填写) |
| interruptOnHurt | false(技能期间霸体) |
在 attackSequence 数组添加一个条目:
clip:Skill_Strike.animdamageSource:拖入对应的DamageSourceSO(定义伤害量/类型)
Step 3:搭建 Prefab
路径:Assets/_Game/Prefabs/Enemies/E002/ENM_HuangZhi.prefab
ENM_HuangZhi (根节点)
├── 组件:EnemyBase ← 注意:不挂 EnemyMovement、NavAgent
├── 组件:EnemySensorHub
├── 组件:Rigidbody2D(Kinematic,Gravity Scale=0)
├── HurtBox/
│ ├── 组件:HurtBox(初始 enabled=false)、Collider2D
└── Abilities/
└── 子节点 CeilingStrike_Ability → 组件:CeilingHangStrikeAbility
⚠️ E002 不挂
EnemyMovement、EnemyNavAgent、NavAgent、TransformBasedMovement,因为它是固定单位。
CeilingHangStrikeAbility 配置:
_config:ABL_E002_CeilingStrike.asset_strikeClip:Skill_Strike.anim_loopClip:Skill_Loop.anim_endClip:Skill_End.anim_attackHitBox:拖入AttackHitBox/子节点的 HitBox 组件_hurtBox:拖入HurtBox/子节点的 HurtBox 组件_hangDuration:脆弱悬挂时长(秒,策划填写)
EnemySensorHub:
| slotName | Sensor 类型 | 说明 |
|---|---|---|
attack_range |
RangeSensor2D | 正下方矩形区域,检测玩家是否在正下方 |
Step 4:行为树
Selector
├── Sequence [死亡]
│ ├── BD_IsStateMatch → Dead
│ └── BD_Wait → Duration: 999
│
└── Sequence [攻击]
├── BD_IsSensorDetecting → SlotName: "attack_range"
├── BD_CanUseAbility → AbilitySO: ABL_E002_CeilingStrike
└── BD_UseAbility → AbilitySO: ABL_E002_CeilingStrike
Step 5:Addressables
- Address:
ENM_HuangZhi· Group:Enemies· Labels:Enemy
6. E003 幼蛭
设计要点:HP=1(一击即死)。支持两种生成路径:
① 场景预置(悬挂天花板,触发器激活)
② E005 肥蛭死亡时从代码生成。
生成后从天花板落下(Kinematic → Dynamic),落地后开始地面追击。
Step 1:动画 Clip
路径:Assets/_Game/Art/Characters/Enemies/E003/Animations/
| Clip 文件名 | 用途 | 绑定位置 |
|---|---|---|
Idle.anim |
天花板吸附待机(循环) | AnimConfig.Idle |
Fall.anim |
下落旋转(循环,直到落地) | AnimatedCeilingDropAbility._fallLoopClip |
Move.anim |
地面移动(循环) | AnimConfig.Walk |
Skill.anim |
追击加速(循环) | AnimConfig.Run |
Death.anim |
死亡 | AnimConfig.Dead |
Step 2:ScriptableObject
路径:Assets/_Game/Data/Enemies/E003/
ENM_E003_Stats.asset:MaxHP=1(一击即死)
ABL_E003_Fall.asset:
| 字段 | 值 |
|---|---|
| abilityId | "e003_fall" |
| cooldown | 0 |
Step 3:Prefab
路径:Assets/_Game/Prefabs/Enemies/E003/ENM_YouZhi.prefab
ENM_YouZhi (根节点)
├── 组件:E003_YouZhi(C# 脚本)、EnemyMovement、EnemyNavAgent、NavAgent、TransformBasedMovement
├── 组件:EnemySensorHub
├── 组件:Rigidbody2D(初始 Body Type=Kinematic,下落能力会自动切换为 Dynamic)
├── HurtBox/
├── ContactDamageZone/
│ └── 组件:BodyContactDamage(初始 enabled=false,落地后由能力自动启用)
└── Abilities/
└── 子节点 Fall_Ability → 组件:AnimatedCeilingDropAbility
AnimatedCeilingDropAbility 配置:
| 字段 | 值 |
|---|---|
_config |
ABL_E003_Fall.asset |
_fallLoopClip |
Fall.anim |
_fallGravityScale |
3.5(策划可调) |
_maxFallTime |
3(超时保护) |
_groundMask |
选择 Ground Layer |
_recoveryTime |
0.1(落地后短暂停顿) |
_contactDamage |
拖入 ContactDamageZone/ 的 BodyContactDamage |
E003_YouZhi 组件配置:
_activateOnSpawn:勾选 ✓(对象池生成时自动触发下落;若是场景预置则由触发器调用ActivateFromCeiling()方法)
EnemySensorHub:
| slotName | 类型 |
|---|---|
aggro |
RangeSensor2D |
Step 4:行为树
Selector
├── Sequence [死亡]
│ ├── BD_IsStateMatch → Dead
│ └── BD_StopMovement
│
├── Sequence [下落(Idle 阶段,一次性)]
│ ├── BD_IsAiPhase → Idle
│ └── BD_UseAbility → ABL_E003_Fall ← 落地后能力内部自动 SetAiPhase(Patrol)
│
├── Sequence [感知追击]
│ ├── BD_IsSensorDetecting → "aggro"
│ └── BD_ChasePlayer ← 自动 SetAiPhase(Chase)+播 AnimConfig.Run
│
└── BD_Patrol ← 兜底巡逻,自动 SetAiPhase(Patrol)+播 AnimConfig.Walk
Step 5:Addressables
- Address:
ENM_YouZhi· Group:Enemies· Labels:Enemy,Poolable,Preload
⚠️ E003 必须标记
Poolable和Preload,因为 E005 死亡时会从对象池动态生成它。
7. E004 蛭母(小Boss)
设计要点:拥有出场演出、三种战斗技能(撕咬/头槌/酸液)、翻身能力(玩家绕后时触发),死亡分两阶段(Death_Pre 无敌演出 → 真正死亡)。
Step 1:动画 Clip
路径:Assets/_Game/Art/Characters/Enemies/E004/Animations/
| Clip 文件名 | 用途 | 绑定位置 |
|---|---|---|
Static.anim |
休眠待机(1帧循环) | AnimConfig.Idle |
Move.anim |
战斗移动(循环) | AnimConfig.Walk |
Appear.anim |
出场嚎叫(单次) | AppearAbility._appearClip |
Skill01.anim |
撕咬(单次) | MeleeAttackAbility.attackSequence[0].clip |
Skill02_Start.anim |
头槌起手(单次) | RepeatSlamAbility._startClip |
Skill02_Loop.anim |
头槌砸地(单次,循环N次) | RepeatSlamAbility._loopClip |
Skill02_End.anim |
头槌收招(单次) | RepeatSlamAbility._endClip |
Skill03.anim |
吐酸液(单次) | ProjectileAttackAbility.attackSequence[0/1].clip |
Flip.anim |
翻身转向(单次) | FacePlayerAbility._faceClip |
Death_Pre.anim |
死亡前摇(循环,约3秒) | E004_ZhiMu._deathPreClip |
Death.anim |
死亡消散(单次) | AnimConfig.Dead |
Step 2:ScriptableObject
路径:Assets/_Game/Data/Enemies/E004/
| 文件名 | abilityId | 关键配置 |
|---|---|---|
ENM_E004_Stats.asset |
— | HP/速度/感知范围 |
ABL_E004_Appear.asset |
"e004_appear" |
cooldown=0 |
ABL_E004_Bite.asset |
"e004_bite" |
preferredMaxRange=近战半径;attackSequence[0] 绑定 DamageSourceSO |
ABL_E004_HeadSlam.asset |
"e004_headslam" |
preferredMaxRange=中距半径;interruptOnHurt=false(霸体) |
ABL_E004_Acid.asset |
"e004_acid" |
preferredMinRange=2 |
ABL_E004_Flip.asset |
"e004_flip" |
cooldown=0.3 |
Step 3:Prefab
路径:Assets/_Game/Prefabs/Enemies/E004/ENM_ZhiMu.prefab
ENM_ZhiMu (根节点)
├── 组件:E004_ZhiMu(C# 脚本)、EnemyMovement、EnemyNavAgent、NavAgent、TransformBasedMovement
├── 组件:EnemySensorHub
├── 组件:EnemyFeedback、Rigidbody2D
├── HurtBox/ → 组件:HurtBox、Collider2D
├── BiteHitBox/ → 组件:HitBox、Collider2D(撕咬用)
├── SlamHitBox/ → 组件:HitBox、Collider2D(头槌用)
├── AcidMuzzle/ → 空 Transform(酸液发射点)
└── Abilities/
├── Appear_Ability → 组件:AppearAbility
├── Bite_Ability → 组件:MeleeAttackAbility
├── HeadSlam_Ability → 组件:RepeatSlamAbility
├── Acid_Ability → 组件:ProjectileAttackAbility
└── Flip_Ability → 组件:FacePlayerAbility
E004_ZhiMu 组件配置:
_deathPreClip:拖入Death_Pre.anim_hurtBox:拖入HurtBox/组件_deathPreDuration:3(与 Death_Pre 动画时长一致,策划调整)
AppearAbility:
_config:ABL_E004_Appear.asset_appearClip:Appear.anim
MeleeAttackAbility(撕咬):
_config:ABL_E004_Bite.asset- 在 ABL_E004_Bite 的
attackSequence[0]中:clip:Skill01.animhitBoxSlot:"bite"(与_hitBoxSlots中的 slotName 对应)hitBoxEnterT:0.30(Clip 时长的 30% 时激活 HitBox)hitBoxExitT:0.60(Clip 时长的 60% 时停用 HitBox)
- 在 MeleeAttackAbility 的
_hitBoxSlots中添加:slotName="bite", hitBox=拖入BiteHitBox/
RepeatSlamAbility(头槌):
_config:ABL_E004_HeadSlam.asset_startClip:Skill02_Start.anim_loopClip:Skill02_Loop.anim_endClip:Skill02_End.anim_hitBox:拖入SlamHitBox/_slamCount:2(策划调整)_hitActiveTime:0.15_staggerDuration:1.2(收招后硬直,供玩家反击)
ProjectileAttackAbility(酸液):
_config:ABL_E004_Acid.asset_muzzle:拖入AcidMuzzle/Transform- ABL_E004_Acid 的
attackSequence[0]:clip:Skill03.animprojectileCount:3(同时发射数量)spreadAngleDeg:30(扩散角)projectileFireT:0.5(Clip 的 50% 时发射)projectileConfig:指向PROJ_ZhiMu_Acid的弹体配置 SO
FacePlayerAbility(翻身):
_config:ABL_E004_Flip.asset_faceClip:Flip.anim
EnemySensorHub:
| slotName | 类型 |
|---|---|
aggro |
RangeSensor2D |
los |
LOSSensor2D |
Step 4:行为树
Selector
├── Sequence [死亡]
│ ├── BD_IsStateMatch → Dead
│ └── BD_StopMovement
│
├── Sequence [出场(仅首次,Idle阶段)]
│ ├── BD_IsAiPhase → Idle
│ └── BD_UseAbility → ABL_E004_Appear ← 内部自动 SetAiPhase(Combat)
│
└── Sequence [战斗]
├── BD_IsAiPhase → Combat
└── Selector
├── Sequence [翻身(技能间隙+玩家在背后)]
│ ├── BD_CanUseAbility → ABL_E004_Flip ← CanUse 内部已检测无技能运行且玩家在背后
│ └── BD_UseAbility → ABL_E004_Flip
│
├── Sequence [撕咬(近距)]
│ ├── BD_CanUseAbility → ABL_E004_Bite, CheckRange=true
│ └── BD_UseAbility → ABL_E004_Bite
│
├── Sequence [头槌(中距)]
│ ├── BD_CanUseAbility → ABL_E004_HeadSlam, CheckRange=true
│ └── BD_UseAbility → ABL_E004_HeadSlam
│
├── Sequence [酸液(远程)]
│ ├── BD_CanUseAbility → ABL_E004_Acid
│ └── BD_UseAbility → ABL_E004_Acid
│
└── BD_MoveToPlayer
⚠️
BD_CanUseAbility(ABL_E004_Flip)无需额外检测"无其他技能运行",该逻辑已内置在FacePlayerAbility.CanUse中。
Step 5:Addressables
- Address:
ENM_ZhiMu· Group:Enemies· Labels:Enemy
8. E005 肥蛭(精英怪)
设计要点:撕咬技能有后摇脆弱窗口(HurtBox 激活),死亡时 Death_Pre 动画中通过 AnimationEvent 生成多只 E003。
Step 1:动画 Clip
路径:Assets/_Game/Art/Characters/Enemies/E005/Animations/
| Clip 文件名 | 用途 | 绑定位置 |
|---|---|---|
Idle.anim |
待机(循环) | AnimConfig.Idle |
Move.anim |
移动(循环) | AnimConfig.Walk/Run |
Skill01.anim |
撕咬(单次) | MeleeVulnerabilityAbility._attackClip |
Skill02.anim |
吐酸液(单次,连续两轮复用) | ProjectileAttackAbility.attackSequence[0/1].clip |
Death_Pre.anim |
死亡前摇(含 AnimationEvent) | E005_FeiZhi._deathPreClip |
Death.anim |
死亡 | AnimConfig.Dead |
⚠️ Death_Pre AnimationEvent 设置(关键步骤):
- 在 Unity 动画编辑器中打开
Death_Pre.anim- 在适当帧(E003 应该出现的时刻)添加 AnimationEvent
- 函数名:
SpawnProjectile,字符串参数:"spawn_e003"
Step 2:ScriptableObject
路径:Assets/_Game/Data/Enemies/E005/
| 文件名 | abilityId | 配置 |
|---|---|---|
ENM_E005_Stats.asset |
— | HP/速度 |
ABL_E005_Bite.asset |
"e005_bite" |
preferredMaxRange=近战半径 |
ABL_E005_Acid.asset |
"e005_acid" |
无范围限制 |
Step 3:Prefab
路径:Assets/_Game/Prefabs/Enemies/E005/ENM_FeiZhi.prefab
ENM_FeiZhi (根节点)
├── 组件:E005_FeiZhi(C# 脚本)、EnemyMovement、EnemyNavAgent、NavAgent、TransformBasedMovement
├── 组件:EnemySensorHub
├── 组件:Rigidbody2D
├── HurtBox/ → 组件:HurtBox
├── BiteHitBox/ → 组件:HitBox
├── AcidMuzzle/ → 空 Transform
└── Abilities/
├── Bite_Ability → 组件:MeleeVulnerabilityAbility
└── Acid_Ability → 组件:ProjectileAttackAbility
E005_FeiZhi 配置:
_deathPreClip:Death_Pre.anim_hurtBox:HurtBox/组件_deathPreDuration:与动画时长一致_spawnCount:3(生成 E003 数量,策划调整)_spawnRadius:1.5(生成散布半径)
MeleeVulnerabilityAbility(撕咬):
_config:ABL_E005_Bite.asset_attackClip:Skill01.anim_hitBox:BiteHitBox/_hurtBox:HurtBox/(后摇期间开放脆弱窗口)_hitEnterT:0.30(攻击 30% 时激活 HitBox)_hitExitT:0.60(攻击 60% 时停用 HitBox)_staggerDuration:1.0(后摇脆弱窗口时长,策划调整)
ProjectileAttackAbility(酸液,连续两次):
_config:ABL_E005_Acid.asset_muzzle:AcidMuzzle/- ABL_E005_Acid 的
attackSequence(需要 2 个条目):[0]:clip=Skill02.anim, projectileCount=1, projectileFireT=0.6, postDelay=0.2[1]:clip=Skill02.anim, projectileCount=1, projectileFireT=0.6
⚠️ 设计要求"连续两次吐出"必须用 2个 attackSequence 条目,每条发射1颗。如果用 projectileCount=2 会同帧射出 2 颗,不是连续动作。
Step 4:行为树
Selector
├── Sequence [死亡]
│ ├── BD_IsStateMatch → Dead
│ └── BD_StopMovement
│
├── Sequence [撕咬(近距)]
│ ├── BD_CanUseAbility → ABL_E005_Bite, CheckRange=true
│ └── BD_UseAbility → ABL_E005_Bite
│
├── Sequence [酸液(远程)]
│ ├── BD_CanUseAbility → ABL_E005_Acid
│ └── BD_UseAbility → ABL_E005_Acid
│
└── BD_ChasePlayer ← 自动 SetAiPhase(Chase)+播 AnimConfig.Run
Step 5:Addressables
- Address:
ENM_FeiZhi· Group:Enemies· Labels:Enemy
9. E006 讙
设计要点:跳跃攻击(动画原地,Rigidbody2D 冲量负责实际位移)。Flip 由巡逻转身自动处理,无需专用 Ability。
Step 1:动画 Clip
路径:Assets/_Game/Art/Characters/Enemies/E006/Animations/
| Clip 文件名 | 用途 | 绑定位置 |
|---|---|---|
Idle.anim |
待机 | AnimConfig.Idle |
Move.anim |
巡逻移动 | AnimConfig.Walk |
Flip.anim |
转身 | AnimConfig.Turn |
Skill.anim |
跳跃攻击(含起跳/飞行/落地姿态) | LeapAttackAbility(attackSequence[0].clip) |
Death.anim |
死亡 | AnimConfig.Dead |
Step 2:ScriptableObject
路径:Assets/_Game/Data/Enemies/E006/
ABL_E006_Leap.asset:
| 字段 | 值 |
|---|---|
| abilityId | "e006_leap" |
| cooldown | 攻击间隔(策划填写) |
| attackSequence[0].clip | Skill.anim |
Step 3:Prefab
路径:Assets/_Game/Prefabs/Enemies/E006/ENM_Huan.prefab
ENM_Huan (根节点)
├── 组件:EnemyBase、EnemyMovement、EnemyNavAgent、NavAgent、TransformBasedMovement
├── 组件:EnemySensorHub
├── 组件:Rigidbody2D(Dynamic)
├── HurtBox/
├── LandingHitBox/ → 组件:HitBox(落地时的攻击判定)
└── Abilities/
└── Leap_Ability → 组件:LeapAttackAbility
EnemyMovement:
_enableTurnAnimation:勾选 ✓
LeapAttackAbility 配置:
_config:ABL_E006_Leap.asset_jumpHeight、_maxRange:策划填写_windupTime:起跳前摇时间(秒)_recoveryTime:落地硬直时间_groundMask:Ground Layer_landingHitBox:拖入LandingHitBox/
EnemySensorHub:
| slotName | 类型 |
|---|---|
aggro |
RangeSensor2D |
wall_ahead |
RaySensor2D |
ledge |
RaySensor2D |
Step 4:行为树
Selector
├── Sequence [死亡]
│ ├── BD_IsStateMatch → Dead
│ └── BD_StopMovement
│
├── Sequence [跳跃攻击]
│ ├── BD_IsSensorDetecting → "aggro"
│ ├── BD_CanUseAbility → ABL_E006_Leap
│ └── BD_UseAbility → ABL_E006_Leap
│
└── BD_Patrol ← wall_ahead + ledge 槽驱动翻转(含 Turn 动画)
Step 5:Addressables
- Address:
ENM_Huan· Group:Enemies· Labels:Enemy
10. Boss 嘲风
设计要点:
- Phase 0(地面):4 技能加权随机(回旋扇/扇形连击/小龙卷/大龙卷)
- Phase 1(空中):HP 低于 50% 触发过渡,进入悬浮阶段,使用风石技能 + 击落机制
- 击落机制:Phase 1 被命中 N 次后触发击落序列(落地硬直→恢复浮空)
- 击败演出:完整的 4 段过场动画序列
Step 1:动画 Clip
路径:Assets/_Game/Art/Characters/Enemies/ChaoFeng/Animations/
状态动画(绑定 AnimConfig):
| Clip 文件名 | AnimConfig 字段 |
|---|---|
Idle.anim |
Idle |
Move.anim |
Walk |
Dead.anim |
Dead |
技能动画(绑定 BossSkillSO.skillAnimation):
| Clip 文件名 | 对应技能 |
|---|---|
Boomerang.anim |
回旋扇主动画 |
Boomerang_End.anim |
回旋扇收招(绑定 ChaoFengBoss._boomerangEndClip) |
FanCombo_01.anim~FanCombo_03.anim |
扇形三连击各段 |
TornadoSmall.anim |
小龙卷 |
TornadoLarge.anim |
大龙卷 |
WindStone.anim |
风石(Phase 2 专属) |
过渡/演出动画(绑定 ChaoFengBoss 字段):
| Clip 文件名 | 绑定字段 |
|---|---|
PhaseTransition.anim |
_phaseTransitionClip(上浮过渡动画) |
KnockdownHit.anim |
ChaoFengKnockdownCounter._knockdownHitClip(被打落时) |
Stagger.anim |
ChaoFengKnockdownCounter._staggerClip(落地硬直,复用为 Defeat_Pant) |
Defeat_Struggle.anim |
_defeatStruggleClip(击败时空中挣扎) |
Defeat_Pant.anim |
_defeatPantClip(倒地喘气) |
Defeat_StandUp.anim |
_defeatStandUpClip(站起) |
Step 2:ScriptableObject(BossSkillSO)
路径:Assets/_Game/Data/Enemies/ChaoFeng/
⚠️ 嘲风使用 BossSkillSO(不是 EnemyAbilitySO)。右键 → Create → BaseGames/Boss/Boss Skill
| 文件名 | skillId | availablePhaseIndices | weight |
|---|---|---|---|
ABL_ChaoFeng_Boomerang.asset |
"boomerang" |
[0] |
1.0 |
ABL_ChaoFeng_FanCombo.asset |
"fan_combo" |
[0] |
1.5 |
ABL_ChaoFeng_TornadoSmall.asset |
"tornado_small" |
[0] |
1.2 |
ABL_ChaoFeng_TornadoLarge.asset |
"tornado_large" |
[0] |
0.8 |
ABL_ChaoFeng_WindStone.asset |
"wind_stone" |
[1] |
Phase 2 专属 |
每个 BossSkillSO 的 skillAnimation 字段绑定对应 Clip。
ENM_ChaoFeng_Stats.asset:HP/速度。
Step 3:弹体 Prefab
在制作嘲风 Prefab 之前,需要先创建弹体。
PROJ_Boomerang.prefab
路径:Assets/_Game/Prefabs/Combat/Projectiles/PROJ_Boomerang.prefab
PROJ_Boomerang (根节点)
├── 组件:ReturnProjectile(C# 脚本,继承 Projectile)
├── 组件:Rigidbody2D(Dynamic)
├── 组件:PooledObject(必须,用于对象池回收)
└── HitBox/ → 组件:HitBox、Collider2D(Layer=EnemyProjectile)
ReturnProjectile 配置:
_maxRange:最大飞行距离(策划填写)_returnSpeed:返回速度(策划填写)_config:弹体配置 SO(ProjectileConfigSO,设置 Speed/Lifetime/DamageSource)
⚠️ Addressables 配置:Address=
PROJ_Boomerang, Group=Projectiles, Labels=Poolable,Preload
其他弹体 Prefab
PROJ_TornadoSmall、PROJ_TornadoLarge、PROJ_WindStone:使用现有 Projectile 脚本,配置方式参考项目中已有的弹体。
Step 4:搭建嘲风 Prefab
路径:Assets/_Game/Prefabs/Enemies/ChaoFeng/ENM_ChaoFeng.prefab
ENM_ChaoFeng (根节点)
├── 组件:ChaoFengBoss(C# 脚本)
├── 组件:EnemyMovement
├── 组件:EnemyNavAgent、NavAgent、TransformBasedMovement
├── 组件:BossSkillExecutor(Boss 技能执行器)
├── 组件:ChaoFengFloatController(浮空控制器)
├── 组件:ChaoFengKnockdownCounter(击落计数器)
├── 组件:EnemyFeedback
├── 组件:Rigidbody2D(Dynamic,击落/落地时由 ChaoFengFloatController 切换为 Kinematic)
├── HurtBox/ → 组件:HurtBox
├── Phase1HitBoxes/
│ ├── FanCombo_HitBox_1/ → HitBox
│ ├── FanCombo_HitBox_2/ → HitBox
│ ├── FanCombo_HitBox_3/ → HitBox
│ └── Tornado_HitBox/ → HitBox
├── Muzzles/
│ ├── BoomerangMuzzle/ → 空 Transform(回旋扇发射点)
│ ├── TornadoMuzzle/ → 空 Transform
│ └── WindStoneMuzzle/ → 空 Transform
└── BehaviorTree [BehaviorDesigner 组件]
ChaoFengBoss 字段配置:
| 字段 | 拖入内容 |
|---|---|
_floatController |
根节点 ChaoFengFloatController 组件 |
_knockdownCounter |
根节点 ChaoFengKnockdownCounter 组件 |
_phaseTransitionClip |
PhaseTransition.anim |
_boomerangEndClip |
Boomerang_End.anim |
_boomerangMuzzle |
Muzzles/BoomerangMuzzle Transform |
_tornadoMuzzle |
Muzzles/TornadoMuzzle Transform |
_windStoneMuzzle |
Muzzles/WindStoneMuzzle Transform |
_defeatStruggleClip |
Defeat_Struggle.anim |
_defeatPantClip |
Defeat_Pant.anim |
_defeatStandUpClip |
Defeat_StandUp.anim |
_defeatPantDuration |
3(倒地喘气时长,策划调整) |
_onDefeatWhiteFlash |
拖入白屏效果的 UnityEvent 接收者(可接 CameraManager 方法) |
ChaoFengFloatController 配置:
| 字段 | 值 |
|---|---|
_floatHeight |
悬空高度(米,策划填写) |
_riseDuration |
上浮时长(推荐约 1.5 秒) |
_fallDuration |
落下时长(推荐约 0.8 秒) |
_rb |
拖入根节点 Rigidbody2D |
ChaoFengKnockdownCounter 配置:
| 字段 | 值 |
|---|---|
_threshold |
击落所需命中次数(策划填写,推荐 8) |
_boss |
拖入根节点 ChaoFengBoss 组件 |
_floatCtrl |
拖入根节点 ChaoFengFloatController 组件 |
_knockdownHitClip |
KnockdownHit.anim |
_staggerClip |
Stagger.anim(与 Defeat_Pant 可共用同一 Clip) |
_staggerDuration |
3(落地硬直时长,策划调整) |
BossBase 相关配置(EnemyBase 父类):
_stats:ENM_ChaoFeng_Stats.asset- 在
BossSkillExecutor组件中,添加所有 5 个 BossSkillSO(Boomerang/FanCombo/TornadoSmall/TornadoLarge/WindStone)
Step 5:搭建嘲风行为树
⚠️ 重要:嘲风只有一棵行为树,不要为 Phase 1 和 Phase 2 创建两棵树。
Selector [嘲风根节点]
│
├── Sequence [Phase 2 (HP < 50%)]
│ ├── BD_IsHPBelow → ratio: 0.5
│ ├── BD_BossPhaseTransition → targetPhase: 1, invincibleDuration: 2.0
│ │ (约 1.5s 上浮 + 0.5s 缓冲;过渡中无敌;完成后 CurrentPhase=1)
│ └── Selector [Phase 2 战斗]
│ ├── BD_UseBossSkillWeighted ← 仅 wind_stone(availablePhaseIndices=[1])
│ └── BD_Wait → Duration: 0.5
│
└── Selector [Phase 1 地面战斗] ← HP > 50% 时走此分支
├── BD_UseBossSkillWeighted ← 4 个 Phase 0 技能加权随机
└── BD_MoveToPlayer
⚠️ 关键架构说明:
BD_BossPhaseTransition必须位于 Phase 2 Sequence 的中间,而非独立的并列节点。- 过渡完成后(
CurrentPhase>=1),BD_BossPhaseTransition每 tick 立即返回 Success,Sequence 继续执行下方的战斗节点。- 若两者为并列节点,过渡完成后父 Selector 只要第一个节点成功就停止,永远不会执行战斗节点。
⚠️
BD_BossPhaseTransition.invincibleDuration必须 ≥_riseDuration+ 缓冲(推荐 2.0 秒)。
上浮动画在无敌期开始时立即播放,确保浮空完成后无敌期才结束。
回旋扇 AnimationEvent 设置
- 打开
Boomerang.anim - 在发射时机帧添加 AnimationEvent
- 函数名:
SpawnProjectile,字符串参数:"boomerang"
其他技能 AnimationEvent 设置
| 技能动画 | AnimationEvent 参数 |
|---|---|
| 小龙卷 | SpawnProjectile("tornado_small") |
| 大龙卷 | SpawnProjectile("tornado_large") |
| 风石 | SpawnProjectile("wind_stone") |
Step 6:Addressables
- Address:
ENM_ChaoFeng· Group:Boss_ChaoFeng· Labels:不加 Enemy 标签
11. Addressables 注册步骤
- 打开
Window → Asset Management → Addressables → Groups - 找到对应 Group(Enemies / Boss_ChaoFeng / Projectiles)
- 将 Prefab 拖入对应 Group
- 在 Address 字段填入规范名称(如
ENM_YouZhi) - 在 Labels 栏勾选对应标签
- 点击
Build → New Build → Default Build Script更新资产
Addressables 汇总表:
| Address | Group | Labels |
|---|---|---|
ENM_CaoZhi |
Enemies | Enemy |
ENM_HuangZhi |
Enemies | Enemy |
ENM_YouZhi |
Enemies | Enemy, Poolable, Preload |
ENM_ZhiMu |
Enemies | Enemy |
ENM_FeiZhi |
Enemies | Enemy |
ENM_Huan |
Enemies | Enemy |
ENM_ChaoFeng |
Boss_ChaoFeng | — |
PROJ_Boomerang |
Projectiles | Poolable, Preload |
PROJ_ZhiMu_Acid |
Projectiles | Poolable, Preload |
PROJ_FeiZhi_Acid |
Projectiles | Poolable, Preload |
PROJ_TornadoSmall |
Projectiles | Poolable, Preload |
PROJ_TornadoLarge |
Projectiles | Poolable, Preload |
PROJ_WindStone |
Projectiles | Poolable, Preload |
12. 常见错误排查
❌ 技能触发后动画不播放
原因:能力 SO(EnemyAbilitySO)的 attackSequence 中 clip 字段未绑定 Clip,或 Ability 组件上的 ClipTransition 字段未绑定。
修复:检查 Inspector,确认所有 ClipTransition 字段已拖入对应 .anim 文件。
❌ BD_UseAbility 没有效果(技能没有执行)
原因 A:Ability 组件没有挂在子节点,而是挂在根节点本身。
修复:将 Ability 组件移到 Abilities/ 子节点。
原因 B:abilityId 与 SO 中的 abilityId 不一致(大小写或拼写错误)。
修复:对照 SO 的 abilityId 字段,确认拼写完全一致。
❌ E003 出现后不下落
原因 A:AnimatedCeilingDropAbility._groundMask 未设置,导致落地检测失败,能力超时后强制结束。
修复:将 Ground Layer 加入 _groundMask。
原因 B:E003_YouZhi._activateOnSpawn 未勾选(对象池路径无法触发下落)。
修复:在 Inspector 勾选 _activateOnSpawn。
❌ E005 死亡后没有生成 E003
原因 A:Death_Pre.anim 中没有 AnimationEvent,或函数名/参数拼写错误。
修复:打开动画编辑器,确认 AnimationEvent 的函数为 SpawnProjectile,参数为 "spawn_e003"(区分大小写)。
原因 B:ENM_YouZhi 未注册到 Addressables 或 Pool Key 拼写错误。
修复:确认 Addressable Address 和对象池 Pool Key 均为 ENM_YouZhi(完全一致)。
❌ E004 Flip 动画不触发
原因:ABL_E004_Flip 的条件由 FacePlayerAbility.CanUse 内置检测(无其他技能运行 + 玩家在背后),不需要在行为树里额外检测。
验证:打开 Runtime 调试,确认 FacePlayerAbility.CanUse 返回 true。常见失效原因是另一个技能仍在执行(IsRunning=true)。
❌ 嘲风 Phase 2 只执行一次过渡,之后不战斗
原因:BD_BossPhaseTransition 节点被放在独立的并列 Selector 下,而不是内嵌在 Phase 2 Sequence 中间。
修复:参照本文档 §10 的行为树结构,确认 BD_BossPhaseTransition 是 Phase 2 Sequence 的中间节点,之后跟着战斗 Selector。
❌ 嘲风上浮后动画还是地面状态
原因:BD_BossPhaseTransition.invincibleDuration 设置过短,浮空还没完成无敌期就结束了,后续被 BT 打断。
修复:将 invincibleDuration 设为 ≥ _riseDuration + 0.5 缓冲(推荐 2.0 秒)。
❌ 回旋扇飞出后不回来
原因 A:ReturnProjectile.SetOwner() 没有被调用,_ownerTransform 为 null。
验证:确认 ChaoFengBoss.SpawnProjectile("boomerang") 中调用了 go.GetComponent<ReturnProjectile>()?.SetOwner(transform)。这是 C# 代码,程序检查,策划确认 AnimationEvent 的 payload 拼写正确("boomerang" 全小写)。
原因 B:PROJ_Boomerang 的 Prefab 根节点没有挂 PooledObject 组件。
修复:添加 PooledObject 组件。
如遇到本文档未涵盖的问题,请联系程序查看
Docs/Plan/小怪与Boss实现计划-01.md中的架构关键约束章节,或查阅Docs/Architecture/07_EnemyModule.md。