From 579499e6784c1342557d84458a03a154f2098fe5 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Mon, 27 Jul 2026 16:45:00 +0800 Subject: [PATCH] =?UTF-8?q?docs(enemy):=20=E8=84=B1=E5=9B=B0=E6=94=B9?= =?UTF-8?q?=E7=94=A8=E6=95=8C=E4=BA=BA=E8=87=AA=E8=BA=AB=E6=AD=A5=E9=80=9F?= =?UTF-8?q?(MoveHorizontal/WalkSpeed),=E7=A7=BB=E9=99=A4=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E9=80=9F=E5=BA=A6=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...2026-07-27-nav-offgraph-recovery-design.md | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/Docs_Dev/superpowers/specs/2026-07-27-nav-offgraph-recovery-design.md b/Docs_Dev/superpowers/specs/2026-07-27-nav-offgraph-recovery-design.md index 7eec55fb..035c446f 100644 --- a/Docs_Dev/superpowers/specs/2026-07-27-nav-offgraph-recovery-design.md +++ b/Docs_Dev/superpowers/specs/2026-07-27-nav-offgraph-recovery-design.md @@ -68,14 +68,30 @@ ```csharp // EnemyNavAgent 新增 [Header("脱图脱困(中心离开烘焙段时自动走回可行走地面)")] -[Tooltip("脱困时的水平移动速度(m/s)。脱图期间无法寻路,只能速度驱动走回地面。")] -[SerializeField] [Min(0f)] private float _offGraphRecoverySpeed = 2f; [Tooltip("脱困持续超过此时长仍未回到导航图,输出一次告警(0 = 不告警)。")] [SerializeField] [Min(0f)] private float _offGraphWarnAfter = 2f; public bool IsOnNavGraph => _navAgent != null && _navAgent.HasValidPosition; ``` +**脱困移动使用敌人自身的移动,不引入独立速度参数。** 巡逻层的方向驱动移动路径为: + +``` +EnemyLocomotion(Pace) _enemy.MoveInDirection(dir) + → PendingInput.MoveDir = dir(不写 MoveSpeed) + → EnemyMovement.ConsumeInput:MoveSpeed == 0 → MoveHorizontal(dir) + → vel.x = dir * _config.WalkSpeed ← 敌人自己 EnemyStatsSO 配的步行速度 +``` + +脱困沿用同一条路径:写 `PendingInput.MoveDir = dir` 且 **显式置 `MoveSpeed = 0f`**、`WantStop = false` +(等价于 `EnemyBase.MoveInDirection(dir)`),从而走 `MoveHorizontal` → 敌人自身 `WalkSpeed`。 + +> ⚠ `MoveSpeed` 是**持久字段**(不逐帧清零),而 `EnemyNavAgent` 的段内移动会把它写成导航速度。 +> 故脱困必须**显式清零** `MoveSpeed`,否则会沿用上一次的导航速度而非敌人自身步速。 + +附带好处:`MoveHorizontal` 自带 `WouldBlockAhead` 夹紧(墙 + 悬崖), +脱困走回平台时天然不会从另一侧冲出去。 + `FixedUpdate` 流程(在现有逻辑之前),**判定顺序即为排除顺序**: 1. **`_navAgent.IsOnLink` → 立即跳过脱困**。⚠ 关键:PB2d 在穿越连接段时会主动把映射置为无效 (`NavAgent.UpdateMappedPosition()` 开头 `if (IsOnLink) { currentMappedPosition = Invalid; return; }`), @@ -83,7 +99,8 @@ public bool IsOnNavGraph => _navAgent != null && _navAgent.HasValidPosition; 2. `mapped = _navAgent.HasValidPosition`;映射正常 → 清脱困计时,走原有逻辑。 3. 未映射且 `!_enemyMovement.IsGrounded` → 处于空中(下落中),不干预(落地后自会重判)。 4. 未映射且着地 → `dir = _enemyMovement.GroundSideDir()`: - - `dir != 0` → 写入 `PendingInput.MoveDir = dir; MoveSpeed = _offGraphRecoverySpeed;`,**return**(跳过段推进/受阻处理)。 + - `dir != 0` → 写入 `PendingInput.MoveDir = dir; MoveSpeed = 0f; WantStop = false;`(=敌人自身步速), + **return**(跳过段推进/受阻处理)。 - `dir == 0` **且两侧脚下都无地** → 异常几何 → 按 `_offGraphWarnAfter` 限频告警,暴露根因,不臆造恢复。 - `dir == 0` **但两侧都有地**(中心下方恰为窄缝等罕见几何)→ 不告警、不干预,交由物理与后续帧自然解决。 @@ -138,7 +155,8 @@ if (blocked && (!_paceBlockedPrev || oppositeFree)) { _paceDir = -_paceDir; ... | 正在穿越 NavLink(`IsOnLink`) | PB2d 本身即置映射无效,**必须最先排除**,否则每次跳/落都被脱困打断(见 §3.1 第 1 步) | | 两侧脚下都无地 | 限频告警(含坐标),不臆造恢复——暴露关卡/物理异常 | | 两侧脚下都有地 | 不告警、不干预(罕见几何,交由物理与后续帧自然解决) | -| 恢复速度配成 0 | 敌人不动 → 由 `_offGraphWarnAfter` 告警暴露(不做隐式兜底) | +| `EnemyStatsSO.WalkSpeed` 配成 0 | 敌人无法脱困(也本就无法巡逻)→ 由 `_offGraphWarnAfter` 告警暴露,不做隐式兜底 | +| 脱困期间需要先转身 | `MoveHorizontal` 内 `if (_isTurning) return;` 会让转身动画期间暂不位移,转身结束后自然继续——符合预期,无需特殊处理 | | 飞行敌人 / 无导航敌人 | `IsOnNavGraph` 恒 true,本机制不介入 | | 脱困期间玩家进入追逐区 | AI 照常切追击态;追击同样经 Nav,脱困先把敌人带回图上再寻路 |