포스트

벡터 정규화와 제곱 비교 사거리 판정

방향과 속도를 분리하면 이동이 한 줄이 된다

'적을 향해 초속 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-upy-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²)로 비용을 줄인다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.