벡터 정규화와 제곱 비교 사거리 판정
방향과 속도를 분리하면 이동이 한 줄이 된다
'적을 향해 초속 300으로 이동'을 코드로 옮기려면 방향과 속도를 분리해야 하고 그게 정규화다. y-up·y-down 좌표계 차이부터 √를 걷어낸 사거리 판정까지 손계산으로 확인했다.
“적을 향해 초속 300으로 이동”이라는 한 줄을 코드로 옮기려면 방향과 속도를 분리해야 하고, 그 분리가 곧 정규화다. 이 글에서는 그 기초를 문제 풀이로 확인한 과정을 이야기하려 한다 — y-up과 y-down 좌표계가 무엇을 뒤집는지, 방향 벡터와 정규화가 어떤 식으로 이동 공식이 되는지, 그리고 타워 디펜스 사거리 판정에서 √를 제곱 비교로 걷어내는 최적화까지다. 손으로 계산한 값을 언리얼 코드로 옮겨 확인하는 순서로 간다.
y-up vs y-down 좌표계
좌표계란?
공간에서 위치를 숫자로 표현하는 기준 체계. 같은 좌표값이라도 좌표계에 따라 화면에서의 위치가 달라진다.
y-up 좌표계 (수학/게임 월드)
1
2
3
4
5
6
7
8
y
↑
|
| (3, 4) ← 오른쪽 위
|
───────┼──────→ x
|
| (3, -2) ← 오른쪽 아래
y값이 클수록 위로 간다 — 수학 그래프와 같은 방식이다. 언리얼 엔진·Unity의 월드 좌표, 물리 엔진, 수학이 이 좌표계를 쓴다.
y-down 좌표계 (화면/스크린 좌표계)
1
2
3
4
5
6
7
───────┼──────→ x
|
| (3, 2) ← 오른쪽 아래
|
| (3, 6) ← 더 아래
↓
y
반대로 y값이 클수록 아래로 간다 — 모니터가 위에서 아래로 스캔하는 방향 그대로다. UI 좌표(UMG, HTML Canvas), 텍스처 UV, DirectX 텍스처 좌표, 이미지 픽셀이 여기에 해당한다.
같은 좌표, 다른 위치
| 좌표 (3, 4) | y-up | y-down |
|---|---|---|
| 화면상 위치 | 오른쪽 위 | 오른쪽 아래 |
| y값 증가 시 | 위로 이동 ↑ | 아래로 이동 ↓ |
언리얼 엔진에서의 좌표계
1
2
3
4
5
6
// 언리얼은 왼손 좌표계 (Left-Handed)
// X = 앞(Forward), Y = 오른쪽(Right), Z = 위(Up)
// → 월드 공간은 Z-up (y-up의 3D 버전)
// UI (UMG)는 화면 좌표계 사용
// → 왼쪽 상단이 (0, 0), 아래로 갈수록 y 증가 (y-down)
y-down → y-up 변환
1
2
3
4
5
6
// 화면 좌표를 월드 좌표로 변환할 때
// y_world = ScreenHeight - y_screen
// 예시: 화면 해상도 1080, 화면 좌표 (300, 200)
int ScreenY = 200;
int WorldY = 1080 - ScreenY; // 880 (위쪽)
복기
같은 (3, 4)라도 어느 좌표계인지에 따라 화면 위치가 달라진다는 게 요점이다.
- y-up: y가 클수록 위 → 게임 월드, 물리, 수학
- y-down: y가 클수록 아래 → 화면 UI, 텍스처, 이미지
- 언리얼: 월드 = Z-up (y-up 계열), UI = y-down
- 변환:
y_world = ScreenHeight - y_screen
문제 1 — 좌표계와 벡터의 이해
조건: 2D 게임에서 캐릭터 A가 (1, 5)에, 적 B가 (4, 1)에 있다.
Q1. A에서 B를 향하는 벡터의 방향
1
2
3
// 방향 벡터 = B - A = (4-1, 1-5) = (3, -4)
// x가 양수(우), y가 음수(하)
// 답: 우하향
Q2. y-up 좌표계에서 B는 A보다 위? 아래?
1
2
3
4
// y-up 좌표계: y값이 클수록 위
// A의 y = 5, B의 y = 1
// B의 y < A의 y → B는 A보다 아래
// 답: 아래
Q3. y-down 좌표계(화면 좌표계)에서 B는 A보다 위? 아래?
1
2
3
4
// y-down 좌표계(화면 좌표계): y값이 클수록 아래
// A의 y = 5, B의 y = 1
// B의 y < A의 y → B는 A보다 위
// 답: 위
| 좌표계 | y값 증가 방향 | 사용처 |
|---|---|---|
| y-up (수학/게임 월드) | 위로 ↑ | 언리얼, Unity 월드 좌표 |
| y-down (화면 좌표계) | 아래로 ↓ | UI, 스크린 좌표, DirectX 텍스처 |
문제 2 — 벡터 연산과 정규화
조건: 플레이어 위치 P = (2, 3), 목표 위치 T = (6, 6)
Q1. P에서 T를 향하는 방향 벡터 d
1
2
3
4
// 방향 벡터 d = T - P = (6-2, 6-3) = (4, 3)
FVector2D P(2, 3);
FVector2D T(6, 6);
FVector2D d = T - P; // (4, 3)
Q2. 방향 벡터 d의 크기(길이)
1
2
// |d| = √(x² + y²) = √(4² + 3²) = √(16 + 9) = √25 = 5
float Length = d.Size(); // 5.0
피타고라스 정리: 벡터의 크기 = √(x² + y²)
Q3. 정규화한 단위 벡터
1
2
3
// 단위 벡터 = d / |d| = (4/5, 3/5) = (0.8, 0.6)
// 단위 벡터의 크기는 항상 1
FVector2D UnitDir = d.GetSafeNormal(); // (0.8, 0.6)
정규화(Normalize): 방향은 유지하면서 크기를 1로 만드는 연산
왜 필요? → 방향과 속도를 분리하기 위해.
단위벡터 × 원하는 속도 = 이동량
Q4. 단위 벡터 방향으로 이동 후 새 위치
1
2
3
4
5
6
7
8
9
// 조건: 속도 10, 시간 간격 dt = 0.5
// 새 위치 = P + 단위벡터 × speed × dt
// = (2, 3) + (0.8, 0.6) × 10 × 0.5
// = (2, 3) + (4, 3)
// = (6, 6)
float Speed = 10.0f;
float dt = 0.5f;
FVector2D NewPos = P + UnitDir * Speed * dt; // (6, 6)
이동 공식:
NewPosition = CurrentPosition + Direction × Speed × DeltaTime이 공식이 게임 루프에서 매 프레임 캐릭터를 이동시키는 핵심 공식
문제 3 — 타워 디펜스 사거리 판정
조건: 타워 위치 (0, 0), 사거리 = 10 / 적 3마리: A(6, 8), B(3, 4), C(8, 7)
Q1. 사거리 안에 들어온 적 판별
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 각 적까지의 거리 = √(x² + y²)
// 적 A: √(6² + 8²) = √(36 + 64) = √100 = 10 → 사거리 이내 ✅
// 적 B: √(3² + 4²) = √(9 + 16) = √25 = 5 → 사거리 이내 ✅
// 적 C: √(8² + 7²) = √(64 + 49) = √113 ≈ 10.63 → 사거리 밖 ❌
float Range = 10.0f;
for (auto& Enemy : Enemies)
{
float Dist = FVector2D::Distance(TowerPos, Enemy.Pos);
if (Dist <= Range)
{
// 공격 가능 대상
}
}
거리 판정 최적화: 실무에서는 √ 연산 비용을 줄이기 위해 제곱 비교를 사용한다.
1
2
3
4
5
6
7
// 최적화된 사거리 판정 (√ 연산 생략)
float RangeSq = Range * Range; // 100
// 적 A: 6² + 8² = 100 <= 100 ✅
// 적 B: 3² + 4² = 25 <= 100 ✅
// 적 C: 8² + 7² = 113 > 100 ❌
if (Enemy.Pos.SizeSquared() <= RangeSq) { ... }
Q2. 가장 가까운 적 → 공격 대상
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 사거리 안의 적: A(거리 10), B(거리 5)
// 가장 가까운 적: B (거리 5)
float MinDist = MAX_FLT;
AEnemy* Target = nullptr;
for (auto& Enemy : InRangeEnemies)
{
float Dist = FVector2D::Distance(TowerPos, Enemy->Pos);
if (Dist < MinDist)
{
MinDist = Dist;
Target = Enemy;
}
}
Q3. 타워에서 공격 대상까지의 정규화된 방향 벡터
1
2
3
4
5
6
7
// 타워(0,0) → B(3,4)
// 방향 벡터 = (3, 4)
// 크기 = √(9 + 16) = √25 = 5
// 단위 벡터 = (3/5, 4/5) = (0.6, 0.8)
FVector2D Dir = (Target->Pos - TowerPos).GetSafeNormal(); // (0.6, 0.8)
// 이 방향으로 투사체 발사!
핵심 공식 요약
| 연산 | 공식 | 게임 활용 |
|---|---|---|
| 방향 벡터 | d = 목표 - 현재 | 적을 향한 방향, AI 추적 |
| 벡터 크기 | |d| = √(x² + y²) | 거리 계산, 사거리 판정 |
| 정규화 | û = d / |d| | 방향과 속도 분리 |
| 이동 | P’ = P + û × speed × dt | 매 프레임 캐릭터/투사체 이동 |
| 거리 판정 (최적화) | x² + y² ≤ R² | 사거리/충돌 판정 (√ 생략) |
같은 날 진행한 팀 프로젝트 컨셉 회의 결과는 이 글의 주제와 달라 덜어냈다.
핵심 요약 — 방향 벡터(목표 − 현재)를 정규화해 방향과 속도를 분리하면
P' = P + û × speed × dt한 공식으로 매 프레임 이동이 된다. 사거리 판정은 √ 대신 제곱 비교(x² + y² ≤ R²)로 비용을 줄인다.