BT5-Dev #89: Enemy 즉시 벽 검출 + 1~3초 대기 영역 (PD 2건)

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 패턴
This commit is contained in:
깃 관리자 2026-05-08 12:39:25 +09:00
parent eeb7ded8ee
commit a5b87534e7
1 changed files with 74 additions and 27 deletions

View File

@ -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)