# 行为树创建指南 — 敌人 / Boss(Behavior Designer Pro) > 面向:在已用脚手架搭好的敌人/Boss Prefab 上,挂载并搭建行为树(BT),跑通「巡逻 → 感知 → 技能」闭环。 > 配套:《小怪与Boss实现计划-01》(每个敌人的目标 BT 结构)、`02_Enemy_Boss_Setup_Guide`。 --- ## 0. 技术栈与前提 | 项 | 说明 | |----|------| | 行为树框架 | **Opsive Behavior Designer Pro**(`com.opsive.behaviordesigner`,运行时组件 `Opsive.BehaviorDesigner.Runtime.BehaviorTree`) | | 编辑器入口 | `Tools/Opsive/Behavior Designer`;或选中带 `BehaviorTree` 组件的对象,在 Inspector 点 **Open** | | 自定义任务 | `Assets/_Game/Scripts/Enemies/AI/BD_*.cs`,分类前缀 `BaseGames/Enemy/*` | | 编译宏 | 全部 BD_ 任务用 `#if GRAPH_DESIGNER` 门控。**已在 ProjectSettings → Standalone 定义 `GRAPH_DESIGNER`**。换其它构建目标需自行补该宏,否则任务不可见。 | **前提**:敌人/Boss Prefab 已由 `SceneObjectPlacerTool` 脚手架生成,已具备 `EnemyBase`/`EnemyMovement`/`PhysicsPerceptionSystem`/`HurtBox`/`HitBox`/`Abilities/*` 与绑定好的 SO。BT 只负责**决策与调用**,不负责动画、不负责具体攻击实现。 --- ## 1. 三条铁律(搭树前必读) 1. **动画所有权**:所有技能/触发型动画由对应 `EnemyAbilityBase` 子类在 `ExecuteCoroutine()` 内 `_animancer.Play()` 播放。**BT 禁止用 `BD_PlayAnimation` 触发技能动画**。Idle/Walk/Run/Dead 由框架随 `SetAiPhase` / `Die()` 自动播放。 2. **能力按 abilityId 调用**:`BD_UseAbility` / `BD_CanUseAbility` / `BD_IsAbilityRunning` 通过 abilityId(或 SO 引用)查找挂在 `Abilities/` 子节点上的能力组件。abilityId 必须全局唯一。 3. **墙/崖不走感知**:`BD_Patrol` 用 `EnemyMovement.IsWallAhead/IsLedgeAhead` 内置射线翻转,**无需感知槽**。感知槽只用于「发现玩家/攻击距离」等(见 §3)。 --- ## 2. 创建一棵行为树(通用步骤) 1. **挂组件**:选中敌人 Prefab 根对象 → `Add Component` → `Behavior Tree`(Opsive)。 2. **打开编辑器**:Inspector 里点 **Open**(或 `Tools/Opsive/Behavior Designer`)。 3. **保存形式**: - **内联**(推荐单怪专用树):直接在该 Prefab 的 BehaviorTree 组件上编辑,随 Prefab 保存。 - **共享图**(多怪复用):在编辑器中保存为独立 Behavior Tree 资产,再在组件上引用。约定路径 `Assets/_Game/Data/AI/BT_{敌人}.asset`,命名 `BT_*`。 4. **搭树**:根用 **Selector**(见 §4 各结构),从任务面板(分类 `BaseGames/Enemy/*`)拖入 BD_ 任务,连边、填字段。 5. **填字段**:`BD_IsAiPhase` 选枚举;`BD_UseAbility`/`BD_CanUseAbility` 填 abilityId 或拖 ABL SO;`BD_IsSensorDetecting` 填槽名(见 §3)。 6. **保存** Prefab / 图资产。进入 Play 验证(§6)。 > ⚠️ **运行前确认**:该 Prefab 的 `EnemyBase._statsSO`/`_animConfig`、各能力 `_config`、感知 `_slots` 均已绑定(脚手架已做)。BT 只是调度层。 --- ## 3. 感知槽位(`SensorSlotNames`) `BD_IsSensorDetecting.m_SlotName` 必须等于 `PhysicsPerceptionSystem` 上配置的槽名,常量定义于 `Assets/_Game/Scripts/Enemies/Perception/SensorSlotNames.cs`: | 常量 | 字符串 | 类型 | 典型用途 | |------|--------|------|---------| | `Aggro` | `aggro` | RangeCircle | 主警戒/追击范围 | | `Alert` | `alert` | RangeCircle | 警觉(比 aggro 小) | | `AttackMelee` | `attack_melee` | RangeCircle | 近战触发距离 | | `AttackRange` | `attack_range` | RangeCircle/BoxCast | 远程/区域触发(E002 用正下方 BoxCast) | | `Patrol` | `patrol` | RangeCircle | 巡逻范围限制 | | `LOS` | `los` | BatchLOS | 无遮挡视线 | | `Sight` | `sight` | Sight | 视锥 + 遮挡("看见玩家",E004/E006/嘲风用) | `m_AnyTarget=false` 时对 `EnemyBase.PlayerTransform` 判定;`true` 时用 `HasAnyDetection`(任意目标)。 --- ## 4. 各敌人/Boss 的目标树结构 > 节点缩进表示父子;`Selector` 选择器(命中即返回),`Sequence` 顺序(全成功才成功)。 > abilityId 已随脚手架生成(见 §5 速查)。`BD_CanUseAbility` 勾 `m_CheckRange=true` 时按 ABL SO 的 preferredMin/MaxRange 校验距离。 ### E001 草蛭(伪装→激活追击→失感知收招→巡逻) ``` Selector ├── Sequence [死亡] : BD_IsStateMatch(Dead) → BD_StopMovement ├── Sequence [激活] │ ├── Selector [可激活]: BD_IsAiPhase(Idle) | BD_IsAiPhase(Patrol) │ ├── BD_IsSensorDetecting("aggro") │ ├── BD_UseAbility("e001_alert") // PlayClipAbility:播激活动画 │ └── BD_SetAiPhase(Chase) ├── Sequence [追击] │ ├── BD_IsAiPhase(Chase) │ └── BD_UseAbility("e001_chase") // ContactChaseAbility:循环追击,失感知内部 SetAiPhase(Patrol) └── Selector [巡逻] // ⚠ 必须 Selector ├── Sequence [待机]: BD_IsAiPhase(Idle) → BD_WaitRandom(min,max) └── Sequence [巡逻]: BD_IsAiPhase(Patrol) → BD_Patrol ``` ### E002 簧蛭(固定陷阱:下方检测→钻出三段+脆弱窗口) ``` Selector ├── Sequence [死亡]: BD_IsStateMatch(Dead) → BD_Wait(999) └── Sequence ├── BD_IsSensorDetecting("attack_range") // 正下方 BoxCast ├── BD_CanUseAbility("e002_ceiling_strike") └── BD_UseAbility("e002_ceiling_strike") // CeilingHangStrikeAbility:出击→脆弱悬挂→收回 ``` ### E003 幼蛭(天花板落下→感知追击→兜底巡逻) ``` Selector ├── Sequence [死亡]: BD_IsStateMatch(Dead) → BD_StopMovement ├── Sequence [下落一次]: BD_IsAiPhase(Idle) → BD_UseAbility("e003_fall") // AnimatedCeilingDropAbility 落地后内部 SetAiPhase(Patrol) ├── Sequence [追击]: BD_IsSensorDetecting("aggro") → BD_ChasePlayer └── BD_Patrol ``` > 预置于天花板的 E003 由场景战斗触发器调用 `EnemyAbilityTrigger.Trigger()`;由 E005 死亡生成的(对象池路径)经 `_executeOnSpawn` 在出生时自动触发,均执行 `e003_fall` 能力。 ### E004 蛭母(出场→战斗循环:Flip/撕咬/头槌/酸液/靠近) ``` Selector ├── Sequence [死亡]: BD_IsStateMatch(Dead) → BD_StopMovement ├── Sequence [出场一次]: BD_IsAiPhase(Idle) → BD_UseAbility("e004_appear") // AppearAbility 内部 SetAiPhase(Combat) ├── Sequence [战斗] │ ├── BD_IsAiPhase(Combat) │ └── Selector │ ├── Sequence [Flip]: BD_CanUseAbility("e004_flip") → BD_UseAbility("e004_flip") // CanUse 内含"无其他技能+玩家在背后" │ ├── Sequence [撕咬]: BD_CanUseAbility("e004_bite", CheckRange) → BD_UseAbility("e004_bite") │ ├── Sequence [头槌]: BD_CanUseAbility("e004_headslam", CheckRange) → BD_UseAbility("e004_headslam") │ ├── Sequence [酸液]: BD_CanUseAbility("e004_acid") → BD_UseAbility("e004_acid") │ └── BD_MoveToPlayer └── BD_MoveToPlayer ``` ### E005 肥蛭(近撕咬+后摇脆弱 / 远酸液 / 追击;死亡生成 E003) ``` Selector ├── Sequence [死亡]: BD_IsStateMatch(Dead) → BD_StopMovement // 死亡生成 E003 由 EnemySpawnerOnEvent(SpawnProjectile 动画事件路由)处理,不走 BT ├── Sequence [撕咬]: BD_CanUseAbility("e005_bite", CheckRange) → BD_UseAbility("e005_bite") ├── Sequence [酸液]: BD_CanUseAbility("e005_acid") → BD_UseAbility("e005_acid") └── BD_ChasePlayer ``` ### E006 讙(视锥感知→跳跃爪击 / 巡逻) ``` Selector ├── Sequence [死亡]: BD_IsStateMatch(Dead) → BD_StopMovement ├── Sequence [跳跃攻击] │ ├── BD_IsSensorDetecting("sight") // 正面视锥 │ ├── BD_CanUseAbility("e006_leap") │ └── BD_UseAbility("e006_leap") // LeapAttackAbility:Rigidbody 冲量 └── BD_Patrol ``` ### 嘲风 Boss(单棵树;Phase2 子树由 HP 守门,过渡节点内嵌) ``` Selector [嘲风] ├── Sequence [Phase 2 (HP<50%)] │ ├── BD_IsHPBelow(0.5) │ ├── BD_BossPhaseTransition(targetPhase=1, invincibleDuration≈2.0) // 首次:Running→Success;之后守护即时 Success │ └── Selector [Phase2 战斗] │ ├── BD_UseBossSkillWeighted // 仅 wind_stone(availablePhaseIndices=[1]) │ └── BD_Wait(0.5) └── Selector [Phase 1 地面战] ├── BD_UseBossSkillWeighted // boomerang/fan_combo/tornado_small/tornado_large(均 phase[0]) └── BD_MoveToPlayer ``` > - **不要**把「过渡」和「Phase2 战斗」做成并列兄弟节点——过渡完成后会永远停在第一个 Success 节点,进不去战斗。必须把 `BD_BossPhaseTransition` 作为 Phase2 Sequence 的**中间**节点。 > - **击落机制不走 BT**:由 `ChaoFengKnockdownCounter`(经 `ChaoFengBoss.OnDamageTaken` 转发)独立驱动。 > - 弹体经技能动画 `AnimationEvent → ChaoFengBoss.SpawnProjectile("boomerang"/"tornado_small"/"tornado_large"/"wind_stone")` 生成并按 ProjectileConfigSO 初始化。 --- ## 5. abilityId / skillId 速查(脚手架已生成) | 敌人 | 能力组件 | abilityId | ABL 资产 | |------|---------|-----------|---------| | E001 | PlayClipAbility / ContactChaseAbility | `e001_alert` / `e001_chase` | `ABL_E001_Alert` / `ABL_E001_Chase` | | E002 | CeilingHangStrikeAbility | `e002_ceiling_strike` | `ABL_E002_CeilingStrike` | | E003 | AnimatedCeilingDropAbility | `e003_fall` | `ABL_E003_Fall` | | E004 | Appear/Melee/RepeatSlam/Projectile/FacePlayer | `e004_appear`/`e004_bite`/`e004_headslam`/`e004_acid`/`e004_flip` | `ABL_E004_*` | | E005 | Melee(后摇脆弱)/Projectile | `e005_bite` / `e005_acid` | `ABL_E005_*` | | E006 | LeapAttack / ContactChase | `e006_leap` / `e006_chase` | `ABL_E006_*` | 嘲风 BossSkillSO(`BD_UseBossSkill`/`BD_UseBossSkillWeighted` 用 skillId): `boomerang` / `fan_combo` / `tornado_small` / `tornado_large`(Phase0)、`wind_stone`(Phase1)。 --- ## 6. 验证 1. 把敌人放进可玩场景(脚手架的 `Place E00X`,或拖 Prefab);确保场景有 `NavSurface` 已烘焙(追击/MoveTo 依赖)。 2. 进入 Play。在 Inspector 看 `EnemyBase` 的 `AiPhase` 是否随逻辑切换(Idle→Chase→Patrol…)。 3. Scene 视图开 Gizmo:`PhysicsPerceptionSystem` 各槽以语义色绘制(aggro 橙 / sight 浅蓝 / attack_* 红粉…),确认范围与玩家触发一致。 4. Behavior Designer 编辑器在 Play 时高亮当前执行节点,逐节点排查卡点。 --- ## 7. 常见坑 | 现象 | 原因 / 解法 | |------|------------| | 巡逻分支用 Sequence 导致待机后不巡逻 | 巡逻必须用 **Selector**(待机/巡逻互斥分支) | | `BD_UseAbility` 立刻返回成功、技能没播完 | 它会等 `IsRunning==false`;确认能力 `ExecuteCoroutine` 正确设置 `Phase`。技能不打断需 ABL SO `interruptOnHurt=false` | | 攻击不触发 | `BD_CanUseAbility` 未勾 `m_CheckRange` 或 ABL 的 preferredRange/cooldown 不当;或感知槽名拼错(须用 `SensorSlotNames` 常量值) | | 找不到 BD_ 任务 | 未定义 `GRAPH_DESIGNER`(当前仅 Standalone 已定义) | | 嘲风进不了 Phase2 战斗 | 过渡节点做成了兄弟节点;须内嵌进 Phase2 Sequence(见 §4) | | E002/固定怪 BT 里写了 Patrol/MoveTo | 固定陷阱无移动,不要加移动节点 | | 翻身/墙崖检测想用感知槽 | 不需要;`BD_Patrol` 内置 `EnemyMovement.IsWallAhead/IsLedgeAhead` | --- ## 8. 小结 行为树 = **条件判断(BD_IsXxx / BD_IsSensorDetecting)+ 调用(BD_UseAbility / BD_SetAiPhase / BD_UseBossSkill*)**。动画、攻击实现、感知计算、移动物理都在框架其它层;BT 保持「薄」。按 §4 的结构逐怪搭建,用 §5 的 id、§3 的槽名填字段,再按 §6 验证即可。