[2025_12_16] 오토배틀 게임 유닛 AI 구현하며 마주친 문제들

2025. 12. 16. 21:37TIL

TIL: 오토배틀 유닛 AI 구현 문제들

문제 1: 전투 중인 유닛이 밀려난다

뒤에서 오는 유닛들이 전투 중인 유닛을 계속 밀어냈다.

해결: 전투 위치를 완전 고정 + IsInCombat 플래그

 
 
csharp
// AttackState.cs
public void Enter(UnitFSM unitFSM)
{
    combatPosition = unit.transform.position;  // 위치 저장
    unit.SetCombatState(true);
}

public void PhysicsExecute(UnitFSM unitFSM)
{
    unit.transform.position = combatPosition;  // 매 프레임 강제 고정
}
```

IsInCombat 플래그로 다른 유닛들이 전투 유닛을 감지하고 강하게 회피한다.

---

## 문제 2: 유닛들이 지그재그로 왔다갔다

0.5초마다 우회 방향을 체크하니 계속 방향이 바뀌었다.
```
t=0.0: 정면 막힘 → 우상 우회
t=0.5: 우상 막힘 → 좌상 우회  
t=1.0: 좌상 막힘 → 우상 우회
→ 유닛이 1→2→1→2 왔다갔다

해결: 8방향 체크 + 방향 고정 시간

 
csharp
private void CheckAndFindDetour()
{
    // 8방향 체크 (정면, 좌상, 좌, 좌하, 후, 우하, 우, 우상)
    bool frontBlocked = IsDirectionBlockedByCombat(directions[0]);
    
    // 정면 뚫렸으면 즉시 복귀
    if (!frontBlocked)
    {
        hasDetour = false;
        return;
    }
    
    // 이미 우회 중이고 1초 안 지났으면 방향 유지
    if (hasDetour && detourTimer < 1f)
    {
        return;
    }
    
    // 빈 공간 찾아서 우회
    for (int i = 0; i < 8; i++)
    {
        if (!IsDirectionBlockedByCombat(directions[i]))
        {
            detourDirection = directions[i];
            hasDetour = true;
            break;
        }
    }
}
```

---

## 문제 3: 궁수가 조금씩 전진한다

적 처치 후 다음 적을 찾는 0.3초 동안 기본 방향(위쪽)으로 전진했다.
```
t=0.0: 적 처치 → MoveState
       타겟 없음 → 위로 전진 X
t=0.3: 적 발견 → 타겟 설정
→ 0.3초 × 2m/s = 0.6m 불필요하게 전진

해결: 타겟 없으면 정지 + AttackState 종료 시 즉시 탐색

 
csharp
// MoveState.cs
public void Execute(UnitFSM unitFSM)
{
    if (currentTarget != null)
    {
        moveDirection = (currentTarget.transform.position - unit.transform.position).normalized;
    }
    else
    {
        moveDirection = Vector3.zero;  // 정지
    }
}

// AttackState.cs
public void Exit(UnitFSM unitFSM)
{
    unit.SetCombatState(false);
    
    // 즉시 다음 적 탐색
    UnitBase nextEnemy = unit.FindNearestEnemy();
    if (nextEnemy != null)
    {
        unit.SetCurrentTarget(nextEnemy);
    }
}

문제 4: 중복 코드

MoveState와 AttackState 모두 적 탐색 코드가 필요했다.

해결: UnitBase로 공통 메서드 추출

 
 
csharp
// UnitBase.cs
public UnitBase FindNearestEnemy()
{
    float detectionRange = Data.unitDetectionRange ?? 5f;
    Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, detectionRange);
    // 가장 가까운 적 반환
}

// State에서 사용
UnitBase enemy = unit.FindNearestEnemy();

배운 점

  • 단순함이 최고: 복잡한 3단계 포위 시스템보다 8방향 체크가 더 자연스러웠다
  • 플래그의 힘: IsInCombat 하나로 여러 시스템에서 활용
  • 즉시 vs 주기적: 중요한 건 즉시(정면 뚫림), 덜 중요한 건 주기적(0.3초마다 적 감지)
  •  

남은 과제

  1. 우선순위 타겟팅: 가까운 적이 아니라 HP 낮은 적 먼저 공격
  2. 대형 유지: 일정한 간격 유지하며 전진
  3. 유닛 생성시 Instantiate가 아니라 ObjectPooling 활용하기