개요
원-원 충돌 감지는 두 원이 겹쳤는지 판단하는 알고리즘입니다. 게임에서 플레이어와 적, 총알과 적 등의 충돌을 감지할 때 자주 사용됩니다.
ArrowController.cs의 충돌 감지 코드
// 화살의 현재 위치를 2D 벡터로 저장
Vector2 p1 = transform.position; // Arrow
// 플레이어의 현재 위치를 2D 벡터로 저장
Vector2 p2 = player.transform.position; // Player
// 화살에서 플레이어로의 방향 벡터를 계산
Vector2 direction = p1 - p2;
// 두 오브젝트 사이의 거리를 계산 (벡터의 크기)
float d = direction.magnitude;
// 화살의 충돌 반경
float r1 = 0.5f; // Arrow radius
// 플레이어의 충돌 반경
float r2 = 1.0f; // Player radius
// 원-원 충돌 감지
if (d < r1 + r2)
{
// 충돌 처리
}
변수(variable) 설명
p1 (화살의 위치)
Vector2 p1 = transform.position; // Arrow
- 의미: 화살의 현재 위치 좌표
- 타입(type):
Vector2(x, y 좌표) - 예시: 화살이 (3, 5) 위치에 있으면
p1 = (3, 5)
p2 (플레이어의 위치)
Vector2 p2 = player.transform.position; // Player
- 의미: 플레이어의 현재 위치 좌표
- 타입(type):
Vector2(x, y 좌표) - 예시: 플레이어가 (1, 2) 위치에 있으면
p2 = (1, 2)
direction (방향 벡터)
Vector2 direction = p1 - p2;
- 의미: 화살에서 플레이어로의 방향 벡터
- 계산:
p1 - p2= 화살 위치 - 플레이어 위치 - 예시:
- p1 = (3, 5), p2 = (1, 2)
- direction = (3-1, 5-2) = (2, 3)
d (거리)
float d = direction.magnitude;
- 의미: 두 점(p1, p2) 사이의 거리
- 계산: 방향 벡터의 크기(magnitude)
- 피타고라스 정리 사용:
d = √(dx² + dy²)
r1 (화살의 충돌 반경)
float r1 = 0.5f; // Arrow radius
- 의미: 화살의 충돌 영역 반지름
- 값: 0.5 유닛
- 용도: 화살의 크기를 고려한 충돌 범위
r2 (플레이어의 충돌 반경)
float r2 = 1.0f; // Player radius
- 의미: 플레이어의 충돌 영역 반지름
- 값: 1.0 유닛
- 용도: 플레이어의 크기를 고려한 충돌 범위
피타고라스 정리와 거리 계산
피타고라스 정리란?
직각삼각형에서 빗변의 제곱은 다른 두 변의 제곱의 합과 같습니다.
a² + b² = c²
여기서:
- a, b: 직각을 이루는 두 변
- c: 빗변
2D 공간에서의 거리 계산
두 점 사이의 거리를 계산할 때 피타고라스 정리를 사용합니다.
거리 = √((x2 - x1)² + (y2 - y1)²)
시각적 설명
p1 (x1, y1)
|
| dy = y1 - y2
|
+-------- p2 (x2, y2)
dx = x1 - x2
- dx: x축 방향 거리 = x1 - x2
- dy: y축 방향 거리 = y1 - y2
- 거리 d: 빗변 = √(dx² + dy²)
Unity에서의 구현
Vector2 direction = p1 - p2;
// direction = (dx, dy) = (x1 - x2, y1 - y2)
float d = direction.magnitude;
// d = √(dx² + dy²)
단계별 계산
// 1. 방향 벡터 계산
Vector2 direction = p1 - p2;
// 예: p1 = (3, 5), p2 = (1, 2)
// direction = (3-1, 5-2) = (2, 3)
// 2. 거리 계산 (magnitude는 내부적으로 피타고라스 정리 사용)
float d = direction.magnitude;
// d = √(2² + 3²) = √(4 + 9) = √13 ≈ 3.606
수동 계산 예제
// p1 = (3, 5), p2 = (1, 2)
float dx = 3 - 1; // dx = 2
float dy = 5 - 2; // dy = 3
// 피타고라스 정리로 거리 계산
float d = Mathf.Sqrt(dx * dx + dy * dy);
// d = √(2² + 3²) = √(4 + 9) = √13 ≈ 3.606
원-원 충돌 판정
충돌 조건
두 원이 충돌하려면 두 원의 중심 사이의 거리가 두 반지름의 합보다 작거나 같아야 합니다.
충돌 조건: d ≤ r1 + r2
시각적 설명
충돌하지 않는 경우
r1 r2
○ ○
| |
| d |
|-----------|
d > r1 + r2 → 충돌 없음
충돌하는 경우
r1 r2
○ ○
| |
|d|
d < r1 + r2 → 충돌!
접촉하는 경우
r1 r2
○○
||
|d|
d = r1 + r2 → 접촉 (충돌로 처리)
코드 구현
// 거리 계산
float d = direction.magnitude;
// 충돌 판정
if (d < r1 + r2)
{
// 충돌 발생!
// HP 감소, 화살 삭제 등 처리
}
실제 예제
예제 1: 충돌하지 않는 경우
// 화살 위치: (0, 5)
Vector2 p1 = new Vector2(0, 5);
// 플레이어 위치: (5, 2)
Vector2 p2 = new Vector2(5, 2);
// 방향 벡터
Vector2 direction = p1 - p2; // (-5, 3)
// 거리
float d = direction.magnitude; // √(25 + 9) = √34 ≈ 5.83
// 반경
float r1 = 0.5f; // 화살
float r2 = 1.0f; // 플레이어
// 충돌 판정
if (d < r1 + r2) // 5.83 < 1.5? → false
{
// 충돌 없음
}
예제 2: 충돌하는 경우
// 화살 위치: (2, 3)
Vector2 p1 = new Vector2(2, 3);
// 플레이어 위치: (1, 2)
Vector2 p2 = new Vector2(1, 2);
// 방향 벡터
Vector2 direction = p1 - p2; // (1, 1)
// 거리
float d = direction.magnitude; // √(1 + 1) = √2 ≈ 1.414
// 반경
float r1 = 0.5f; // 화살
float r2 = 1.0f; // 플레이어
// 충돌 판정
if (d < r1 + r2) // 1.414 < 1.5? → true
{
// 충돌 발생!
Debug.Log("화살에 맞았습니다!");
}
성능 최적화
거리 제곱 사용
실제 거리를 계산하지 않고 거리의 제곱을 사용하면 Mathf.Sqrt() 연산을 생략할 수 있어 성능이 향상됩니다.
// ❌ 느린 방법: 실제 거리 계산
float d = direction.magnitude; // √(dx² + dy²) 계산
if (d < r1 + r2) { }
// ✅ 빠른 방법: 거리 제곱 사용
float dSquared = direction.sqrMagnitude; // dx² + dy² (제곱근 없음)
float radiusSum = r1 + r2;
if (dSquared < radiusSum * radiusSum) { }
최적화된 코드
// 방향 벡터 계산
Vector2 direction = p1 - p2;
// 거리 제곱 계산 (제곱근 없이)
float dSquared = direction.sqrMagnitude;
// 반경 합의 제곱
float radiusSum = r1 + r2;
float radiusSumSquared = radiusSum * radiusSum;
// 충돌 판정 (제곱으로 비교)
if (dSquared < radiusSumSquared)
{
// 충돌 처리
}
반경 값 조정
반경 값의 의미
반경 값은 오브젝트의 실제 크기보다 약간 크게 설정하는 것이 좋습니다.
// 화살의 실제 크기가 0.3이라면
float r1 = 0.5f; // 약간 여유를 둔 값
// 플레이어의 실제 크기가 0.8이라면
float r2 = 1.0f; // 약간 여유를 둔 값
반경 값 테스트
// Inspector에서 조정 가능하도록
public float arrowRadius = 0.5f;
public float playerRadius = 1.0f;
void Update()
{
float d = direction.magnitude;
if (d < arrowRadius + playerRadius)
{
// 충돌 처리
}
}
정리
변수(variable) 요약
| 변수(variable) | 의미 | 계산 방법 |
|---|---|---|
| p1 | 화살의 위치 | transform.position |
| p2 | 플레이어의 위치 | player.transform.position |
| direction | 방향 벡터 | p1 - p2 = (dx, dy) |
| d | 두 점 사이의 거리 | √(dx² + dy²) (피타고라스 정리) |
| r1 | 화살의 충돌 반경 | 0.5f |
| r2 | 플레이어의 충돌 반경 | 1.0f |
충돌 판정 공식
충돌 조건: d < r1 + r2
여기서:
d = √((x1 - x2)² + (y1 - y2)²) (피타고라스 정리)
핵심 개념
- 피타고라스 정리: 두 점 사이의 거리 계산에 사용
- magnitude: 벡터의 크기 (거리)
- 원-원 충돌: 거리 < 반지름 합
- 성능 최적화: 거리 제곱 사용 (sqrMagnitude)
연습 문제
-
화살이 (3, 5) 위치에 있고, 플레이어가 (1, 2) 위치에 있을 때 거리를 계산하세요.
-
r1 = 0.5, r2 = 1.0일 때, 거리가 1.2인 경우 충돌이 발생하는지 판정하세요.
-
성능 최적화를 위해 거리 제곱을 사용하는 코드로 변경하세요.
-
반경 값을 Inspector에서 조정할 수 있도록 public 변수(variable)로 변경하세요.
-
충돌이 발생했을 때 거리와 반경 값을 Debug.Log로 출력하는 코드를 추가하세요.