From a5b87534e7cdaf4d2c94768e5b9c041f6a87120a Mon Sep 17 00:00:00 2001 From: swrring Date: Fri, 8 May 2026 12:39:25 +0900 Subject: [PATCH] =?UTF-8?q?BT5-Dev=20#89:=20Enemy=20=EC=A6=89=EC=8B=9C=20?= =?UTF-8?q?=EB=B2=BD=20=EA=B2=80=EC=B6=9C=20+=201~3=EC=B4=88=20=EB=8C=80?= =?UTF-8?q?=EA=B8=B0=20=EC=98=81=EC=97=AD=20(PD=202=EA=B1=B4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PD 보고 (2026-05-08): 1. 벽 영역 버벅대다 밀림 → 즉시 반대 방향 의무 2. patrol 1회 후 1~3초 대기 패턴 정정 (BT89): 1. UpdatePatrol() 영역 분리 — patrol·벽·절벽·대기 통합 2. 수평 벽 Raycast 부활: - bounds 외부 (halfWidth + 0.05m) 시작 - distance 0.1m (총 0.15m·인접 Tile X) - 검출 시 phase+2 즉시 전환 (stuckTimer 100ms 영역 폐기 — 즉시) 3. 절벽 검출: - 검출 시 phase+2 즉시 전환 4. _waitTimer 영역 신규: - patrol arrive 시 Random.Range(1, 3)초 대기 - 벽 검출 시 동일 (대기 후 반대 방향 patrol) - 절벽 검출 시 동일 - 대기 중 control.move.x = 0 5. stuckTimer 보조 영역 잔존 (수평 Raycast 미감지 fallback) 6. Inspector 노출: wallCheckDistance·waitMinTime·waitMaxTime 효과: - 벽 영역 = 즉시 반대 방향 (Raycast 검출 frame에 phase+2) - 절벽 영역 = 즉시 반대 방향 - patrol arrive·벽·절벽 = 1~3초 random 대기 후 다음 phase - 대기 중 = control.move.x = 0 (Idle) - 시각상 자연스러운 patrol 패턴 --- Assets/Scripts/Mechanics/EnemyController.cs | 101 ++++++++++++++------ 1 file changed, 74 insertions(+), 27 deletions(-) diff --git a/Assets/Scripts/Mechanics/EnemyController.cs b/Assets/Scripts/Mechanics/EnemyController.cs index 320dce0..8083730 100644 --- a/Assets/Scripts/Mechanics/EnemyController.cs +++ b/Assets/Scripts/Mechanics/EnemyController.cs @@ -29,14 +29,18 @@ namespace Platformer.Mechanics public float cliffCheckDistance = 0.3f; // 발 앞 절벽 검출 거리 (BT86: 0.6→0.3 — 더 빠른 검출·낭떠러지 근처 Enemy 보호) public float cliffCheckDepth = 1.5f; // 발 아래 Raycast 거리 public LayerMask groundLayerMask = (1 << 0) | (1 << 16); // Layer 0 (지면) + Layer 16 (발판) - public float stuckThresholdTime = 0.1f; // 벽 정지 검출 시간 (BT87: 0.3→0.1 — 움찔거림 차단·즉시 방향 전환) + public float stuckThresholdTime = 0.1f; // 벽 정지 검출 시간 (보조·수평 Raycast 미감지 시 fallback) public float stuckMoveThreshold = 0.02f; // 정지 판정 거리 임계값 (미세 밀림 검출) + public float wallCheckDistance = 0.1f; // BT89: 수평 벽 Raycast distance (bounds 외부 0.05+0.1=0.15m·인접 Tile 검출 X) + public float waitMinTime = 1f; // BT89: patrol arrive 후 대기 random 영역 + public float waitMaxTime = 3f; private float _startX; private float _targetX; private int _patrolPhase; // 0: right out / 1: right back / 2: left out / 3: left back - private float _lastX; // BT81: 벽 정지 검출용 - private float _stuckTimer; // BT81: 벽 정지 누적 시간 + private float _lastX; // 벽 정지 검출용 + private float _stuckTimer; // 벽 정지 누적 시간 + private float _waitTimer; // BT89: arrive 후 대기 누적 시간 (1~3초 random) internal PatrolPath.Mover mover; // legacy 호환·미사용 internal AnimationController control; @@ -99,46 +103,58 @@ namespace Platformer.Mechanics } } - void Update() + // BT89 — patrol·벽·절벽 검출·대기 영역 통합 처리 + void UpdatePatrol() { - // PD 명시 2026-05-08 — PatrolPath 영역 폐기·자동 patrol (생성 위치 기준 좌/우 random 100~150 왕복) + 절벽 검출 + // 대기 영역 — control.move.x = 0 + Timer 감소 + if (_waitTimer > 0f) + { + _waitTimer -= Time.deltaTime; + if (control != null) control.move.x = 0f; + _lastX = transform.position.x; + _stuckTimer = 0f; + return; + } + float dx = _targetX - transform.position.x; + // patrol arrive — 1~3초 대기 후 다음 phase if (Mathf.Abs(dx) < patrolArriveThreshold) { _patrolPhase = (_patrolPhase + 1) % 4; SetNextPatrolTarget(); - dx = _targetX - transform.position.x; + _waitTimer = Random.Range(waitMinTime, waitMaxTime); + if (control != null) control.move.x = 0f; + _lastX = transform.position.x; + _stuckTimer = 0f; + return; } float moveDir = Mathf.Sign(dx); - // 벽 정지·미세 밀림 검출 — BT88: phase+2 (반대 방향 patrol·시작 위치 복귀 폐기) - if (Mathf.Abs(transform.position.x - _lastX) < stuckMoveThreshold) + // 즉시 벽 검출 — 수평 Raycast (bounds 외부 + 0.1m·인접 Tile X) → phase+2 즉시 전환 + if (_collider != null) { - _stuckTimer += Time.deltaTime; - if (_stuckTimer > stuckThresholdTime) + float halfWidth = _collider.bounds.extents.x; + Vector2 wallOrigin = new Vector2( + _collider.bounds.center.x + moveDir * (halfWidth + 0.05f), + _collider.bounds.center.y + ); + RaycastHit2D wallHit = Physics2D.Raycast(wallOrigin, new Vector2(moveDir, 0), wallCheckDistance, groundLayerMask); + if (wallHit.collider != null) { _patrolPhase = (_patrolPhase + 2) % 4; SetNextPatrolTarget(); - dx = _targetX - transform.position.x; - moveDir = Mathf.Sign(dx); + _waitTimer = Random.Range(waitMinTime, waitMaxTime); + if (control != null) control.move.x = 0f; _stuckTimer = 0f; + _lastX = transform.position.x; + return; } } - else - { - _stuckTimer = 0f; - } - _lastX = transform.position.x; - // BT85 — 수평 벽 Raycast 영역 폐기 (PD 보고 2026-05-08: 모든 Enemy 정지) - // BT83/BT84 영역 = bounds 외부+0.5m 영역도 옆 Tile (평지 인접) 검출 → 거짓 양성 → patrol X - // 정정: 수평 Raycast 폐기 + stuckTimer 영역 0.5초 (BT81 영역 회복) — 벽 정지 시 다음 phase - - // 절벽 검출 — BT88: phase+2 (반대 방향 patrol·시작 위치 복귀 폐기) - // 시작 위치 복귀 영역 = 시작 위치도 절벽 근처 시 도중 떨어짐 → phase+2로 즉시 반대 방향 - if (Mathf.Abs(dx) > patrolArriveThreshold && _collider != null) + // 절벽 검출 — 발 앞 Raycast 지면 X 시 phase+2 즉시 전환 (대기) + if (_collider != null) { Vector2 footAhead = new Vector2( _collider.bounds.center.x + moveDir * cliffCheckDistance, @@ -149,13 +165,44 @@ namespace Platformer.Mechanics { _patrolPhase = (_patrolPhase + 2) % 4; SetNextPatrolTarget(); - dx = _targetX - transform.position.x; - moveDir = Mathf.Sign(dx); + _waitTimer = Random.Range(waitMinTime, waitMaxTime); + if (control != null) control.move.x = 0f; _stuckTimer = 0f; + _lastX = transform.position.x; + return; } } - control.move.x = Mathf.Clamp(dx, -1, 1); + // stuckTimer 보조 — 수평 Raycast 미감지 fallback (벽 정지 + 미세 밀림 0.1초 후 phase+2) + if (Mathf.Abs(transform.position.x - _lastX) < stuckMoveThreshold) + { + _stuckTimer += Time.deltaTime; + if (_stuckTimer > stuckThresholdTime) + { + _patrolPhase = (_patrolPhase + 2) % 4; + SetNextPatrolTarget(); + _waitTimer = Random.Range(waitMinTime, waitMaxTime); + if (control != null) control.move.x = 0f; + _stuckTimer = 0f; + _lastX = transform.position.x; + return; + } + } + else + { + _stuckTimer = 0f; + } + _lastX = transform.position.x; + + if (control != null) control.move.x = Mathf.Clamp(dx, -1, 1); + } + + void Update() + { + // BT89 — 자동 patrol + 즉시 벽 검출 + 1~3초 대기 영역 + UpdatePatrol(); + + // 이하 — Player 충돌 검출 영역 (patrol 영역과 분리) // PD 지시 2026-05-07 — Player ↔ Enemy 통과 가능이지만 Bounds.Intersects로 매 프레임 감지 if (_cachedPlayer == null)