UI 系统
This commit is contained in:
200
Docs/Guides/08_BehaviorTree_Authoring_Guide.md
Normal file
200
Docs/Guides/08_BehaviorTree_Authoring_Guide.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# 行为树创建指南 — 敌人 / 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 由场景战斗触发器调用 `E003_YouZhi.ActivateFromCeiling()`;由 E005 死亡生成的走 `OnSpawn()`,均自动触发 `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 由 E005_FeiZhi.SpawnProjectile(AnimationEvent) 处理,不走 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 验证即可。
|
||||
@@ -47,22 +47,32 @@
|
||||
- **固定位置敌人(E002 簧蛭)不挂 EnemyNavAgent / NavAgent,不调用 MoveTo**
|
||||
- 朝向翻转:`EnemyMovement.UpdateFacing()` 优先用 `SpriteRenderer.flipX`,无 SR 时用 `localScale.x`
|
||||
|
||||
### 4. 感知系统约束(SensorToolkit)
|
||||
### 4. 感知系统约束(自研 PhysicsPerceptionSystem)
|
||||
|
||||
`EnemySensorHub` 标准槽位名称约定:
|
||||
> ⚠️ **本节已更新**:项目已**自研 `PhysicsPerceptionSystem` 完全替代 SensorToolkit**,不再使用 `EnemySensorHub` / SensorToolkit 命名空间。`_Game` 代码零引用 SensorToolkit。Prefab 上挂的是 `PhysicsPerceptionSystem`(纯物理实现),其 `_slots[]` 槽位数组配置各感知槽。
|
||||
|
||||
| 槽名 | Sensor 类型 | 用途 |
|
||||
|------|------------|------|
|
||||
| `aggro` | RangeSensor2D | 主要警戒/感知范围 |
|
||||
| `attack_melee` | RangeSensor2D | 近战攻击距离触发 |
|
||||
| `attack_range` | RangeSensor2D | 远程攻击距离触发 |
|
||||
| `los` | LOSSensor2D | 视线检测 |
|
||||
| `wall_ahead` | RaySensor2D | 前方障碍物检测 |
|
||||
| `ledge` | RaySensor2D | 前方悬崖检测 |
|
||||
**架构**:
|
||||
- 接口 `BaseGames.Enemies.Perception.IPerceptionSystem`(`HasSlot` / `IsDetecting(slot, target)` / `HasAnyDetection(slot)` / `GetFirstDetection(slot)`)
|
||||
- 实现 `PhysicsPerceptionSystem`:支持 7 种槽位类型 `RangeCircle / BatchLOS / FanCast / BoxCast / Sight / RayCast / TriggerZone`,含错帧更新(tickInterval)、动态禁用、Enter/Exit 事件;视线由 `SightBatchSystem` 全局预算管理(LOD)
|
||||
- `EnemyBase.Awake()` 通过 `GetComponentInChildren<IPerceptionSystem>()` 注册,对外暴露 `EnemyBase.SensorHub`(`IPerceptionSystem` 类型)
|
||||
|
||||
`BD_IsSensorDetecting` 的 m_SlotName 必须与 Inspector 中 `EnemySensorHub._slots` 对应的 slotName 完全一致。
|
||||
**标准槽位常量**(定义于 `Perception/SensorSlotNames.cs`,禁止散布魔法字符串):
|
||||
|
||||
`BD_Patrol` 优先读取 `wall_ahead` 和 `ledge` 槽的 SensorToolkit 结果;若未配置这两个槽,自动回退 Raycast 兜底。
|
||||
| 常量 | 字符串键 | 槽位类型 | 用途 |
|
||||
|------|---------|---------|------|
|
||||
| `SensorSlotNames.Aggro` | `aggro` | RangeCircle | 主要警戒/追击范围 |
|
||||
| `SensorSlotNames.Alert` | `alert` | RangeCircle | 警觉半径(比 aggro 小,待机/巡逻→Alert 切换) |
|
||||
| `SensorSlotNames.AttackMelee` | `attack_melee` | RangeCircle | 近战攻击距离触发 |
|
||||
| `SensorSlotNames.AttackRange` | `attack_range` | RangeCircle | 远程攻击距离触发 |
|
||||
| `SensorSlotNames.Patrol` | `patrol` | RangeCircle | 巡逻范围限制(超出触发返回) |
|
||||
| `SensorSlotNames.LOS` | `los` | BatchLOS | 无遮挡视线(OverlapCircle + 单射线遮挡) |
|
||||
| `SensorSlotNames.Sight` | `sight` | Sight | 视锥 + 强制 LOS 遮挡检测("看见玩家"核心传感器) |
|
||||
|
||||
> ⚠️ **`wall_ahead` / `ledge` 槽已废除**:前方墙体/悬崖检测**不再走感知系统**,改由 `EnemyMovement.IsWallAhead` / `EnemyMovement.IsLedgeAhead`(EnemyMovement 内置物理射线)提供。
|
||||
|
||||
`BD_IsSensorDetecting` 实现:查询 `gameObject.GetComponent<IPerceptionSystem>()`;字段 `m_SlotName`(应填 `SensorSlotNames` 对应字符串键)+ `m_AnyTarget`(true 时用 `HasAnyDetection`,false 时对 `EnemyBase.PlayerTransform` 调 `IsDetecting`)。
|
||||
|
||||
`BD_Patrol` 直接读取 `EnemyMovement.IsWallAhead / IsLedgeAhead` 触发翻转,**不依赖任何感知槽**(含转身冷却防抖)。
|
||||
|
||||
### 5. AI 阶段(AiPhase)枚举值
|
||||
|
||||
@@ -162,7 +172,7 @@
|
||||
|
||||
**`ENM_E001_Stats.asset`**(EnemyStatsSO):MaxHP、WalkSpeed(巡逻)、RunSpeed(追击)、DetectRange。
|
||||
|
||||
**`ABL_E001_Activate.asset`**:abilityId=`"e001_activate"`,cooldown=0
|
||||
**`ABL_E001_Alert.asset`**:abilityId=`"e001_alert"`,cooldown=1.5(激活/警觉序列,PlayClipAbility)
|
||||
**`ABL_E001_Chase.asset`**:abilityId=`"e001_chase"`,cooldown=0,preferredMaxRange=DetectRange
|
||||
|
||||
#### 新建 Ability
|
||||
@@ -187,7 +197,8 @@ protected override IEnumerator ExecuteCoroutine()
|
||||
[SerializeField] private Animancer.ClipTransition _loopClip;
|
||||
[SerializeField] private Animancer.ClipTransition _endClip;
|
||||
[SerializeField] private BodyContactDamage _contactDamage;
|
||||
[SerializeField] private EnemySensorHub _sensorHub;
|
||||
[SerializeField] private string _aggroSlotName = SensorSlotNames.Aggro;
|
||||
// 感知通过 EnemyBase.SensorHub(IPerceptionSystem)查询,不自挂感知组件引用
|
||||
|
||||
protected override IEnumerator ExecuteCoroutine()
|
||||
{
|
||||
@@ -198,7 +209,8 @@ protected override IEnumerator ExecuteCoroutine()
|
||||
while (true)
|
||||
{
|
||||
if (_enemy.PlayerTransform == null) break;
|
||||
if (!_sensorHub.IsDetecting("aggro", _enemy.PlayerTransform.gameObject)) break;
|
||||
if (_enemy.SensorHub != null &&
|
||||
!_enemy.SensorHub.IsDetecting(_aggroSlotName, _enemy.PlayerTransform.gameObject)) break;
|
||||
_enemy.MoveTo(_enemy.PlayerTransform.position);
|
||||
yield return null;
|
||||
}
|
||||
@@ -216,12 +228,12 @@ protected override IEnumerator ExecuteCoroutine()
|
||||
`
|
||||
E001_CaoZhi
|
||||
├── [EnemyBase] [EnemyMovement] [EnemyNavAgent] [NavAgent] [TransformBasedMovement]
|
||||
├── [EnemySensorHub] → slots: [{aggro, RangeSensor2D}, {wall_ahead, RaySensor2D}, {ledge, RaySensor2D}]
|
||||
├── [PhysicsPerceptionSystem] → _slots: [{aggro, RangeCircle}] ← 墙/崖检测由 EnemyMovement 内置射线,无需槽
|
||||
├── [Rigidbody2D] Dynamic [Collider2D]
|
||||
├── HurtBox/ [HurtBox] [Collider2D trigger=true Layer=EnemyHurtBox]
|
||||
├── ContactDamageZone/ [BodyContactDamage] [HitBox] [Collider2D trigger=true Layer=EnemyHitBox]
|
||||
└── Abilities/
|
||||
├── [PlayClipAbility] (_config=ABL_E001_Activate)
|
||||
├── [PlayClipAbility] (_config=ABL_E001_Alert)
|
||||
└── [ContactChaseAbility] (_config=ABL_E001_Chase)
|
||||
`
|
||||
|
||||
@@ -235,7 +247,7 @@ Selector
|
||||
│ │ ├── BD_IsAiPhase(Idle)
|
||||
│ │ └── BD_IsAiPhase(Patrol)
|
||||
│ ├── BD_IsSensorDetecting("aggro")
|
||||
│ ├── BD_UseAbility(ABL_E001_Activate)
|
||||
│ ├── BD_UseAbility(ABL_E001_Alert)
|
||||
│ └── BD_SetAiPhase(Chase)
|
||||
├── Sequence [追击]
|
||||
│ ├── BD_IsAiPhase(Chase)
|
||||
@@ -312,7 +324,7 @@ protected override void OnInterrupted(InterruptReason reason)
|
||||
`
|
||||
E002_HuangZhi
|
||||
├── [EnemyBase] ← 无 EnemyMovement,无 NavAgent
|
||||
├── [EnemySensorHub] → slots: [{attack_range, RangeSensor2D(正下方矩形)}]
|
||||
├── [PhysicsPerceptionSystem] → _slots: [{attack_range, BoxCast(正下方矩形)}]
|
||||
├── [Rigidbody2D] Kinematic Gravity=0
|
||||
├── HurtBox/ [HurtBox enabled=false] [Collider2D trigger]
|
||||
├── AttackHitBox/ [HitBox] [Collider2D trigger]
|
||||
@@ -441,7 +453,7 @@ public void ActivateFromCeiling()
|
||||
`
|
||||
E003_YouZhi
|
||||
├── [E003_YouZhi] [EnemyMovement] [EnemyNavAgent] [NavAgent] [TransformBasedMovement]
|
||||
├── [EnemySensorHub] → slots: [{aggro, RangeSensor2D}]
|
||||
├── [PhysicsPerceptionSystem] → _slots: [{aggro, RangeCircle}]
|
||||
├── [Rigidbody2D] Kinematic(AnimatedCeilingDropAbility 切换为 Dynamic)
|
||||
├── HurtBox/
|
||||
├── ContactDamageZone/ [BodyContactDamage enabled=false] ← 落地后由能力 Enable
|
||||
@@ -646,7 +658,7 @@ private IEnumerator DeathSequence()
|
||||
`
|
||||
E004_ZhiMu
|
||||
├── [E004_ZhiMu] [EnemyMovement] [EnemyNavAgent] [NavAgent] [TransformBasedMovement]
|
||||
├── [EnemySensorHub] → slots: [{aggro, RangeSensor2D}, {los, LOSSensor2D}]
|
||||
├── [PhysicsPerceptionSystem] → _slots: [{aggro, RangeCircle}, {sight, Sight}]
|
||||
├── [EnemyFeedback] [Rigidbody2D]
|
||||
├── HurtBox/ BiteHitBox/ SlamHitBox/
|
||||
├── AcidMuzzle/ [Transform]
|
||||
@@ -823,7 +835,7 @@ attackSequence[0].clip:Skill ClipTransition
|
||||
`
|
||||
E006_Huan
|
||||
├── [EnemyBase] [EnemyMovement] [EnemyNavAgent] [NavAgent] [TransformBasedMovement]
|
||||
├── [EnemySensorHub] → slots: [{aggro, RangeSensor2D}, {wall_ahead, RaySensor2D}, {ledge, RaySensor2D}]
|
||||
├── [PhysicsPerceptionSystem] → _slots: [{sight, Sight(正面视锥)}] ← 墙/崖检测由 EnemyMovement 内置射线
|
||||
├── [Rigidbody2D] Dynamic
|
||||
├── HurtBox/ LandingHitBox/
|
||||
└── Abilities/ [LeapAttackAbility (abilityId="e006_leap")]
|
||||
@@ -835,10 +847,10 @@ E006_Huan
|
||||
Selector
|
||||
├── BD_IsStateMatch(Dead) → BD_StopMovement ← Die() 已自动播放 AnimConfig.Dead
|
||||
├── Sequence [跳跃攻击]
|
||||
│ ├── BD_IsSensorDetecting("aggro")
|
||||
│ ├── BD_IsSensorDetecting("sight") ← 正面视锥感知(FanCast/Sight),对应设计"视线检测正面扇形"
|
||||
│ ├── BD_CanUseAbility(ABL_E006_Leap)
|
||||
│ └── BD_UseAbility(ABL_E006_Leap)
|
||||
└── BD_Patrol [wall_ahead+ledge槽驱动翻转,无传感器则Raycast兜底]
|
||||
└── BD_Patrol [EnemyMovement.IsWallAhead/IsLedgeAhead 内置射线驱动翻转,无需感知槽]
|
||||
`
|
||||
|
||||
> **Flip 动画**:`EnemyMovement` 已原生支持转身动画,无需修改 BT。只需:
|
||||
@@ -1164,8 +1176,8 @@ public class ChaoFengKnockdownCounter : MonoBehaviour
|
||||
public void OnBossHit(DamageInfo info)
|
||||
{
|
||||
if (_inKnockdown || _boss.CurrentPhase != 1) return;
|
||||
// ⚠️ 玩家是否在空中的判断方式待确认(见Q6)
|
||||
// 临时实现:所有命中均计数
|
||||
// Q6 已实现:仅玩家在空中(IGroundedActor.IsGrounded==false)时计数
|
||||
if (!IsPlayerAirborne()) return;
|
||||
_count++;
|
||||
if (_count >= _threshold) { _count = 0; StartCoroutine(KnockdownSequence()); }
|
||||
}
|
||||
@@ -1189,6 +1201,7 @@ public class ChaoFengKnockdownCounter : MonoBehaviour
|
||||
`
|
||||
ChaoFeng
|
||||
├── [ChaoFengBoss] [EnemyMovement] [EnemyNavAgent] [NavAgent] [TransformBasedMovement]
|
||||
├── [PhysicsPerceptionSystem] → _slots: [{aggro, RangeCircle}, {sight, Sight}]
|
||||
├── [BossSkillExecutor]
|
||||
├── [ChaoFengFloatController]
|
||||
├── [ChaoFengKnockdownCounter]
|
||||
@@ -1228,7 +1241,7 @@ Assets/_Game/
|
||||
├── Data/Enemies/
|
||||
│ ├── E001/
|
||||
│ │ ├── ENM_E001_Stats.asset ← EnemyStatsSO(ENM_ 前缀)
|
||||
│ │ ├── ABL_E001_Activate.asset ← PlayClipAbility SO(ABL_ 前缀)
|
||||
│ │ ├── ABL_E001_Alert.asset ← PlayClipAbility SO(ABL_ 前缀,abilityId=e001_alert)
|
||||
│ │ └── ABL_E001_Chase.asset ← ContactChaseAbility SO
|
||||
│ ├── E002/
|
||||
│ │ ├── ENM_E002_Stats.asset
|
||||
@@ -1328,8 +1341,8 @@ Assets/_Game/
|
||||
| Q1 | 嘲风 Phase 2 能否用 Phase 1 技能? | BossSkillSO.availablePhaseIndices |
|
||||
| Q2 | 击落计数是否打断风石施法? | ChaoFengKnockdownCounter 中断逻辑 |
|
||||
| Q3 | 挥扇三连第3击是否有后方碰撞盒? | AttackPatternSO HitBox 形状 |
|
||||
| Q4 | E004 Flip 背后检测方案(Sensor槽还是 FacingDirection 比较)? | EnemySensorHub 配置 |
|
||||
| Q4 | E004 Flip 背后检测方案(Sensor槽还是 FacingDirection 比较)? | 现实现:FacePlayerAbility.CanUse 内用 Movement.FacingDirection 比较,不依赖感知槽 |
|
||||
| Q5 | E005 是否有 Flip 动画? | 是否新建 Flip Ability |
|
||||
| Q6 | 玩家空中判断方式(DamageInfo 携带/PlayerController 事件)? | ChaoFengKnockdownCounter.IsAttackerAirborne |
|
||||
| Q6 | ~~玩家空中判断方式~~ | ✅ 已实现:`IGroundedActor` 接口(Core)由 PlayerMovement 实现;`ChaoFengKnockdownCounter` 经 `BossBase.PlayerTransform` 查询,仅玩家离地时计数(取不到接口时保守按空中处理) |
|
||||
| Q7 | 各角色 HP/速度/伤害/CD 数值 | 所有 StatsSO / AbilitySO 填写 |
|
||||
| Q8 | 嘲风 Phase 2 浮空待机是否复用 Phase 1 Idle? | 美术制作量 |
|
||||
Reference in New Issue
Block a user