From 9a61d3e256443668dc931fda4616ddbcd164491e Mon Sep 17 00:00:00 2001 From: Joywayer Date: Mon, 27 Jul 2026 11:55:55 +0800 Subject: [PATCH] =?UTF-8?q?refactor(enemy):=20=E7=A7=BB=E5=8A=A8=E5=B1=82?= =?UTF-8?q?=E6=8E=A2=E6=B5=8B=E4=B8=8E=20EdgeSafeMargin=20=E6=94=B9?= =?UTF-8?q?=E8=B5=B0=20Body=20=E6=9D=83=E5=A8=81,=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E7=9A=84=E7=A2=B0=E6=92=9E=E4=BD=93=E8=A7=A3?= =?UTF-8?q?=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/_Game/Scripts/Enemies/EnemyMovement.cs | 90 +++++++++---------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/Assets/_Game/Scripts/Enemies/EnemyMovement.cs b/Assets/_Game/Scripts/Enemies/EnemyMovement.cs index 700cabe7..4e8fec53 100644 --- a/Assets/_Game/Scripts/Enemies/EnemyMovement.cs +++ b/Assets/_Game/Scripts/Enemies/EnemyMovement.cs @@ -42,8 +42,6 @@ namespace BaseGames.Enemies [SerializeField] private float _navJumpMaxHeight = 6f; [Tooltip("可处理的最大跳跃水平距离")] [SerializeField] private float _navJumpMaxDist = 10f; - [Tooltip("用于确定射线起点宽度和底边的 Collider2D;留空则 Awake 时自动查找")] - [SerializeField] private Collider2D _groundCheckCollider; [Tooltip("从碰撞体底边向下的射线检测距离")] [SerializeField] private float _groundCheckDist = 0.15f; [Tooltip("射线数量(1 = 仅中心,>1 时沿碰撞体底边均匀分布)")] @@ -67,6 +65,17 @@ namespace BaseGames.Enemies private int _facingDir = 1; private Coroutine _linkCoroutine; + // 身体几何权威(EnemyBase.Body)。懒获取以支持编辑器模式(Awake 未执行)下的 Gizmo/探测预览。 + private EnemyBase _enemy; + private IEnemyBody Body + { + get + { + if (_enemy == null) _enemy = GetComponent(); + return _enemy != null ? _enemy.Body : null; + } + } + // ── 转身状态 ──────────────────────────────────────────────────────── private bool _isTurning; private int _pendingFacingDir; // 转身目标方向,转身完成后 ApplyFacingFlip 使用 @@ -184,12 +193,10 @@ namespace BaseGames.Enemies private Vector2 GetGroundRayOrigin(int index) { - // 优先用序列化字段,编辑器模式下 Awake 未执行时也能直接 GetComponent - var col = _groundCheckCollider != null ? _groundCheckCollider : GetComponent(); - if (col == null) - return (Vector2)transform.position; + var body = Body; + if (body == null || body.Collider == null) return (Vector2)transform.position; - Bounds b = col.bounds; + Bounds b = body.Bounds; float x = _groundCheckCount <= 1 ? b.center.x : Mathf.Lerp(b.min.x, b.max.x, (float)index / (_groundCheckCount - 1)); @@ -206,31 +213,23 @@ namespace BaseGames.Enemies return false; } - // 墙体射线起点:碰撞体朝向侧边缘中心高度 + // 墙体射线起点:身体朝向侧边缘、中心高度 private Vector2 GetWallRayOrigin() { - var col = _groundCheckCollider != null ? _groundCheckCollider : GetComponent(); - if (col == null) return (Vector2)transform.position; - Bounds b = col.bounds; - float x = _facingDir >= 0 ? b.max.x : b.min.x; - return new Vector2(x, b.center.y); + var body = Body; + if (body == null || body.Collider == null) return (Vector2)transform.position; + return new Vector2(body.FrontEdgeX(_facingDir), body.Bounds.center.y); } - // 悬崖射线起点:碰撞体前下角再向前偏移 _ledgeCheckFwdOffset + // 悬崖射线起点:身体前下角再向前偏移 _ledgeCheckFwdOffset private Vector2 GetLedgeRayOrigin() { - var col = _groundCheckCollider != null ? _groundCheckCollider : GetComponent(); - if (col == null) return (Vector2)transform.position; - Bounds b = col.bounds; - float x = _facingDir >= 0 - ? b.max.x + _ledgeCheckFwdOffset - : b.min.x - _ledgeCheckFwdOffset; - return new Vector2(x, b.min.y); + var body = Body; + if (body == null || body.Collider == null) return (Vector2)transform.position; + float sign = _facingDir >= 0 ? 1f : -1f; + return new Vector2(body.FrontEdgeX(_facingDir) + sign * _ledgeCheckFwdOffset, body.Bounds.min.y); } - private Collider2D GroundColliderOrSelf() - => _groundCheckCollider != null ? _groundCheckCollider : GetComponent(); - /// /// 朝 前进是否会撞墙或让碰撞体前缘越过地形边缘(墙 或 悬崖)。 /// 用碰撞体前缘(bounds.max.x / min.x)判定,天然考虑碰撞体宽度—— @@ -242,37 +241,36 @@ namespace BaseGames.Enemies /// 前缘水平方向是否有墙。 public bool WouldHitWallAhead(float dir) { - 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; - return Physics2D.Raycast(new Vector2(edgeX, b.center.y), new Vector2(sign, 0f), _wallCheckDist, _wallMask); + var body = Body; + if (body == null || body.Collider == null || _wallCheckDist <= 0f) return false; + float sign = Mathf.Sign(dir); + return Physics2D.Raycast(new Vector2(body.FrontEdgeX(dir), body.Bounds.center.y), + new Vector2(sign, 0f), _wallCheckDist, _wallMask); } /// 前缘外是否为悬崖(脚边地面缺失,会让碰撞体越出地形边缘)。 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); + var body = Body; + if (body == null || body.Collider == null || _ledgeCheckDownDist <= 0f) return false; + float sign = Mathf.Sign(dir); + return !Physics2D.Raycast( + new Vector2(body.FrontEdgeX(dir) + sign * _ledgeCheckFwdOffset, body.Bounds.min.y), + Vector2.down, _ledgeCheckDownDist, _groundMask); } /// - /// 身体中心到"能站住的平台边缘最近点"的水平安全余量 = 碰撞体水平半宽 + 悬崖检测前向偏移。 - /// WouldFallAhead 在"碰撞体前缘 + _ledgeCheckFwdOffset"处探地,故身体中心必须离平台边缘至少此距离, - /// 否则前缘探出崖沿被夹停。供 nav 段内随机游走把随机点从段两端各内缩此值,避免随机到身体站不住的崖沿点。 + /// 身体中心到"能站住的平台边缘最近点"的水平安全余量 = 身体半宽 + 悬崖探测前向偏移。 + /// 半宽取自唯一权威 ;探测偏移是本移动层自己的射线调参。 + /// WouldFallAhead 在"身体前缘 + _ledgeCheckFwdOffset"处探地,故身体中心必须离平台边缘至少此距离, + /// 否则前缘探出崖沿被夹停。供 nav 段内随机游走/目标点吸附把点从段两端内缩此值。 /// public float EdgeSafeMargin { get { - var col = GroundColliderOrSelf(); - float halfWidth = col != null ? col.bounds.extents.x : 0f; + var body = Body; + float halfWidth = body != null ? body.HalfWidth : 0f; return halfWidth + Mathf.Max(0f, _ledgeCheckFwdOffset); } } @@ -302,8 +300,6 @@ namespace BaseGames.Enemies Debug.LogError($"[EnemyMovement] '{name}' 启用了墙检测(_wallCheckDist>0)但 _wallMask 为 Nothing," + "撞墙判定将失效(会穿墙)。请在 Inspector 配置墙体层(推荐 Platform + Wall)。", this); _rb = GetComponent(); - if (_groundCheckCollider == null) - _groundCheckCollider = GetComponent(); // 从 Sprite 或 localScale 的初始状态推断朝向,并统一切换为 localScale 翻转。 // 这样子对象(含 RaySensor2D)会随 localScale 正确翻转,不再依赖 flipX。 @@ -331,8 +327,8 @@ namespace BaseGames.Enemies transform.localScale = new Vector3(signX, s.y, s.z); // 将 Visual 子节点的 localPosition 对齐到 Collider2D offset,使视觉中心与碰撞体中心重合 - if (_visualRoot != null && _groundCheckCollider != null) - _visualRoot.localPosition = _groundCheckCollider.offset; + if (_visualRoot != null && Body != null && Body.Collider != null) + _visualRoot.localPosition = Body.Collider.offset; if (_enableTurnAnimation) { @@ -722,7 +718,7 @@ namespace BaseGames.Enemies } // 2. 对齐 localPosition 到 Collider2D offset - var col = _groundCheckCollider != null ? _groundCheckCollider : GetComponent(); + var col = Body != null ? Body.Collider : GetComponent(); if (col != null) { UnityEditor.Undo.RecordObject(visual, "Set Visual LocalPosition");