[2025_12_26] 공격 범위 계산 버그 - 좌표 기준점 설정의 중요점
2025. 12. 26. 20:51ㆍTIL
문제 상황
오토배틀 게임에서 마법사(AOE 공격 유닛)가 사거리 밖에 있는 넥서스를 공격하는 버그 발생
버그
- 마법사가 넥서스와 멀리 떨어져 있음 (사거리 5, 실제 거리 10+)
- "넥서스 피격중" 로그가 계속 출력됨
- 넥서스 HP가 지속적으로 감소
원인 분석
잘못된 코드
csharp
private void OnAOEAttack()
{
Vector3 targetPos = unit.GetTargetPosition(); // 넥서스 위치
if (unit.CurrentTargetNexus != null)
{
// targetPos가 넥서스 위치이므로 distance = 0
float distanceToNexus = Vector3.Distance(targetPos, unit.CurrentTargetNexus.transform.position);
if (distanceToNexus <= aoeRadius) // 항상 true
{
unit.CurrentTargetNexus.TakeDamage(damage); // 버그 발생
}
}
}
문제점
- AOE 중심(targetPos) = 넥서스 위치
- 거리 체크도 넥서스 위치 기준
- 결과: Distance(넥서스, 넥서스) = 0 → 항상 AOE 범위 내로 판정
- 유닛의 실제 사거리는 전혀 체크되지 않음
해결 방법
핵심: 두 가지 거리를 분리해서 체크
csharp
private void OnAOEAttack()
{
if (!unit.IsTargetInAttackRange()) return; // 1. 사거리 체크 (Execute에서도 확인)
Vector3 targetPos = unit.GetTargetPosition(); // 타겟 위치
Vector3 attackCenter = targetPos; // AOE 중심
float aoeRadius = unit.Data.AOERadius ?? 3f;
float damage = unit.CurAtk;
if (unit.CurrentTargetNexus != null)
{
// 2. 유닛과 넥서스 사이의 거리 (사거리 체크)
float distanceFromUnit = Vector3.Distance(unitTransform.position, unit.CurrentTargetNexus.transform.position);
float attackRange = unit.Data.unitAttackRange ?? 3f;
// 3. 사거리 내에 있는지 먼저 확인
if (distanceFromUnit <= attackRange)
{
// 4. AOE 범위 내에 있는지 확인
float distanceFromAOECenter = Vector3.Distance(attackCenter, unit.CurrentTargetNexus.transform.position);
if (distanceFromAOECenter <= aoeRadius)
{
unit.CurrentTargetNexus.TakeDamage(damage);
}
}
}
}
```
체크 순서
1. Execute()에서 사거리 체크** (`IsTargetInAttackRange()`)
2. OnAOEAttack() 초반에 한 번 더 사거리 체크
3. 유닛 위치 ↔ 넥서스 거리 확인 (사거리)
4. AOE 중심 ↔ 넥서스 거리확인 (AOE 범위)
---
Before & After
Before (버그)
```
마법사 위치: (0, 0)
넥서스 위치: (15, 0) // 사거리 5 밖!
AOE 중심: (15, 0) // 넥서스 위치
Distance(AOE 중심, 넥서스) = 0
→ 0 <= 3 (AOE 반경) 데미지 발생! (버그)
```
After (정상)
```
마법사 위치: (0, 0)
넥서스 위치: (15, 0)
사거리: 5
Distance(마법사, 넥서스) = 15
→ 15 > 5 거리 밖이므로 공격 불가 (정상)
배운 점
1. 좌표 기준점을 명확히 하자
- AOE 중심, 사거리 체크, 거리 계산 등 각각의 기준점이 무엇인지 명확히 정의
- 특히 "거리"를 계산할 때는 "어디서 어디까지의 거리인가?" 항상 확인
2. 같은 대상을 다른 기준으로 체크해야 할 때 주의
csharp
// 잘못된 예
Vector3 center = target.position;
float distance = Vector3.Distance(center, target.position); // 의미 없음!
// 올바른 예
float distanceFromUnit = Vector3.Distance(unit.position, target.position);
float distanceFromAOECenter = Vector3.Distance(aoeCenter, target.position);
3. 다층 검증의 중요성
- Execute()에서 1차 사거리 체크
- Attack() 직전 2차 사거리 체크
- OnAOEAttack() 내부에서 3차 상세 체크
- 중복되더라도 안전장치를 여러 겹 두는 것이 좋다
4. 버그 발견 팁
- 로그에 거리 값을 출력해서 예상과 다른지 확인
- Debug.Log($"거리: {distance:F2}, 사거리: {attackRange}") 같은 식으로 수치 비교
추가 개선사항
공격 직전에도 사거리 재확인 추가:
csharp
public void Execute(UnitFSM unitFSM)
{
// ...
if (attackTimer >= 1f / attackSpeed)
{
attackTimer = 0f;
// 공격 직전 사거리 재확인!
if (unit.IsTargetInAttackRange())
{
Attack();
unit.TryUseSkill();
}
}
}
결론
"거리 계산은 항상 기준점이 2개 필요하다*는 기본을 놓쳐서 발생한 버그였다. 특히 AOE나 범위 공격처럼 여러 좌표 기준이 섞이는 경우, 각 거리 계산의 의미를 명확히 하고 로그로 검증하는 습관이 중요하다.
'TIL' 카테고리의 다른 글
| [2025_12_30] Unity UI 이벤트 구독/해제 (0) | 2025.12.30 |
|---|---|
| [2025_12-29] 유닛 떨림 현상 해결 - 유닛 회피 시스템 (0) | 2025.12.29 |
| [2025_12_24] 컴포지션 패턴 (0) | 2025.12.24 |
| [2025_12_23] 에셋 정하기 (0) | 2025.12.23 |
| [2025_12_22] 모의면접 피드백 (1) | 2025.12.22 |