From f1ca62f881c76dbc0b563e3835b1644264570b6f Mon Sep 17 00:00:00 2001 From: Joywayer Date: Fri, 10 Jul 2026 15:48:58 +0800 Subject: [PATCH] =?UTF-8?q?@=20feat(enemy):=20=E8=BF=BD=E5=87=BB(nav)?= =?UTF-8?q?=E4=B9=9F=E5=A4=B9=E7=B4=A7=E6=82=AC=E5=B4=96=E8=BE=B9=E7=BC=98?= =?UTF-8?q?=E2=80=94=E2=80=94=E7=A2=B0=E6=92=9E=E4=BD=93=E5=89=8D=E7=BC=98?= =?UTF-8?q?=E4=B8=8D=E8=B6=8A=E5=9C=B0=E5=BD=A2=E8=BE=B9=E7=BC=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MoveWithSpeed(导航跟段驱动追击)原先无边缘夹紧,仅靠 nav 图,agent 按点定位 可让碰撞体前缘越出段/地形边缘约半个碰撞体宽。现加"悬崖"夹紧(WouldFallAhead, 按碰撞体前缘即时判定),贴地生效:追击时碰撞体边缘停在地形边缘、不越界,与 Pace 一致。只夹悬崖不夹墙(墙交寻路绕行,免误夹停);跨沟的 nav-link 跳/落不经 MoveWithSpeed(仅 IsMovingOnSegment 时写速度),故不影响跳跃/下落过沟。 WouldBlockAhead 拆为 WouldHitWallAhead / WouldFallAhead 复用;PlayMode 校验 拆分正确(前缘朝悬崖侧 WouldFall=True)、Pace 不回归。 Co-Authored-By: Claude Opus 4.8 (1M context) @ --- Assets/_Game/Scripts/Enemies/EnemyMovement.cs | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/Assets/_Game/Scripts/Enemies/EnemyMovement.cs b/Assets/_Game/Scripts/Enemies/EnemyMovement.cs index 622a4f41..d6125847 100644 --- a/Assets/_Game/Scripts/Enemies/EnemyMovement.cs +++ b/Assets/_Game/Scripts/Enemies/EnemyMovement.cs @@ -226,33 +226,39 @@ namespace BaseGames.Enemies return new Vector2(x, b.min.y); } + private Collider2D GroundColliderOrSelf() + => _groundCheckCollider != null ? _groundCheckCollider : GetComponent(); + /// - /// 朝 前进是否会撞墙或让碰撞体前缘越过地形边缘。 + /// 朝 前进是否会撞墙或让碰撞体前缘越过地形边缘(墙 或 悬崖)。 /// 用碰撞体前缘(bounds.max.x / min.x)判定,天然考虑碰撞体宽度—— /// 保证"碰撞体边缘不越过地形边缘"。用当前请求方向即时射线,避免缓存朝向过期。 /// 供移动层夹紧与巡逻 Pace 翻向共用同一判定,避免"夹停了却不掉头"。 /// - public bool WouldBlockAhead(float dir) + public bool WouldBlockAhead(float dir) => WouldHitWallAhead(dir) || WouldFallAhead(dir); + + /// 前缘水平方向是否有墙。 + public bool WouldHitWallAhead(float dir) { - var col = _groundCheckCollider != null ? _groundCheckCollider : GetComponent(); - if (col == null) return false; + var col = GroundColliderOrSelf(); + if (col == null || _wallCheckDist <= 0f) return false; Bounds b = col.bounds; float sign = Mathf.Sign(dir); - float edgeX = sign >= 0f ? b.max.x : b.min.x; // 朝向侧碰撞体前缘 + float edgeX = sign >= 0f ? b.max.x : b.min.x; LayerMask wallLayer = (_wallMask.value != 0) ? _wallMask : _groundMask; + return Physics2D.Raycast(new Vector2(edgeX, b.center.y), new Vector2(sign, 0f), _wallCheckDist, wallLayer); + } - // 墙:从前缘水平探;命中则前方有墙 - if (_wallCheckDist > 0f && - Physics2D.Raycast(new Vector2(edgeX, b.center.y), new Vector2(sign, 0f), _wallCheckDist, wallLayer)) - return true; - - // 悬崖:从"前缘 + 前探偏移"处向下探地;无地面则前缘外是悬崖 - if (_ledgeCheckDownDist > 0f && - !Physics2D.Raycast(new Vector2(edgeX + sign * _ledgeCheckFwdOffset, b.min.y), - Vector2.down, _ledgeCheckDownDist, _groundMask)) - return true; - - return false; + /// 前缘外是否为悬崖(脚边地面缺失,会让碰撞体越出地形边缘)。 + public bool WouldFallAhead(float dir) + { + var col = GroundColliderOrSelf(); + if (col == null || _ledgeCheckDownDist <= 0f) return false; + Bounds b = col.bounds; + float sign = Mathf.Sign(dir); + float edgeX = sign >= 0f ? b.max.x : b.min.x; + return !Physics2D.Raycast(new Vector2(edgeX + sign * _ledgeCheckFwdOffset, b.min.y), + Vector2.down, _ledgeCheckDownDist, _groundMask); } private void WallAndLedgeCheck() @@ -416,7 +422,11 @@ namespace BaseGames.Enemies public void MoveWithSpeed(float dir, float speed) { if (_isTurning) return; - UpdateFacing(dir); + if (dir != 0f) UpdateFacing(dir); + // 地面怪追击/导航:前缘将越过地形边缘(悬崖)则不前进——碰撞体边缘停在地形边缘、不越界。 + // 只夹"悬崖"不夹"墙"(墙交给寻路绕行,免得贴墙路径被误夹停);跨沟的 nav-link 跳/落 + // 不经本方法(EnemyNavAgent 仅在 IsMovingOnSegment 时写 MoveSpeed),故不影响跳跃/下落过沟。 + if (dir != 0f && IsGrounded && WouldFallAhead(dir)) dir = 0f; var vel = _rb.velocity; vel.x = dir * speed; _rb.velocity = vel;