게임 수학 핵심 정리

게임에서 수학은 별도의 기능이 아니다.

캐릭터가 이동하고, 카메라가 회전하고, 총알이 날아가고, 적이 플레이어를 추적하고, 물체가 충돌하는 모든 과정에 수학이 들어간다.

특히 게임 개발에서 자주 사용하는 수학은 다음과 같이 연결해서 볼 수 있다.

좌표 -> 벡터 -> 삼각함수 -> 행렬 -> 회전 -> 보간 -> 거리/충돌 -> 물리 -> 확률

모든 수학을 깊게 공부할 필요는 없다. 게임에서 어떤 문제를 해결할 때 어떤 수학을 사용하는지 이해하는 것이 더 중요하다.

좌표계

게임에서 가장 기본이 되는 것은 좌표다.

2차원에서는 보통

(x, y)

3차원에서는

(x, y, z)

를 사용한다.

어떤 점의 위치를 좌표로 표현할 수 있다.

Player = (10, 3, 5)
Enemy  = (20, 3, 8)

하지만 게임에서는 하나의 좌표계만 사용하는 것이 아니다.

대표적으로

Local Space -> World Space -> View Space -> Clip Space -> Screen Space

와 같이 여러 공간을 사용한다.

예를 들어 캐릭터의 팔 위치는 캐릭터를 기준으로 하는 Local Space에 있을 수 있고, 실제 게임 월드에서의 위치는 World Space에 존재한다.

좌표 변환을 이해하는 것은 게임 수학의 기본이다.

벡터

벡터는 게임 수학에서 가장 많이 사용하는 개념이다.

벡터는 크기와 방향을 표현한다.

Vector2 = (x, y)
Vector3 = (x, y, z)

게임에서는 다음과 같은 값들이 대부분 벡터로 표현된다.

위치
방향
속도
가속도
힘
이동량
법선

두 위치가 있을 때 이동 방향은

direction = target - current

로 구할 수 있다.

예를 들어

current = (2, 1, 3)
target  = (7, 1, 5)

direction = (5, 0, 2)

방향만 필요하다면 정규화한다.

direction = Normalize(target - current)

벡터의 크기

벡터의 크기는 피타고라스 정리에서 나온다.

2차원 벡터

v = (x, y)

의 크기는

|v| = sqrt(x² + y²)

3차원에서는

|v| = sqrt(x² + y² + z²)

이다.

게임에서는 거리 계산에 직접 사용한다.

distance = |target - player|

다만 단순히 거리를 비교하는 경우에는 제곱근을 계산할 필요가 없다.

distance² = x² + y² + z²

두 점 사이의 거리가 10보다 가까운지 확인한다면

distance² < 100

으로 비교할 수 있다.

불필요한 sqrt를 피할 수 있기 때문에 게임 코드에서 자주 사용하는 방식이다.

정규화

정규화는 벡터의 크기를 1로 만드는 것이다.

Normalize(v) = v / |v|

방향만 필요할 때 사용한다.

예를 들어

direction = target - player

만 사용하면 대상과의 거리가 멀어질수록 벡터의 크기도 커진다.

이를 정규화하면

direction = Normalize(target - player)

가 되고 순수한 방향 벡터로 사용할 수 있다.

다만 길이가 0인 벡터는 정규화할 수 없다.

Normalize((0, 0, 0))

같은 경우를 코드에서 처리해야 한다.

내적

내적은 두 벡터의 방향 관계를 계산하는 데 사용한다.

a · b = axbx + ayby + azbz

또는

a · b = |a||b|cosθ

정규화된 벡터라면

a · b = cosθ

이다.

따라서 내적의 결과를 이용해 두 방향이 얼마나 가까운지 판단할 수 있다.

1   -> 같은 방향
0   -> 직각
-1  -> 반대 방향

게임에서는 매우 많이 사용한다.

예를 들어 플레이어가 적을 바라보고 있는지 확인할 수 있다.

forward = Player Forward
direction = Normalize(Enemy - Player)

dot = Dot(forward, direction)

dot이 특정 값보다 크다면 적이 플레이어의 시야각 안에 있다고 판단할 수 있다.

외적

외적은 3차원 벡터 두 개를 이용해서 두 벡터에 모두 수직인 벡터를 구한다.

a × b

게임에서는 주로 다음에 사용한다.

법선 벡터 계산
좌우 방향 계산
회전 방향 판단
평면 방향 계산
3차원 좌표계 구성

예를 들어 Forward와 Up으로 Right를 만들 수 있다.

Right = Forward × Up

내적이 방향 사이의 관계를 판단하는 데 강하다면 외적은 새로운 방향을 만드는 데 많이 사용한다.

삼각함수

삼각함수는 각도와 길이의 관계를 계산한다.

대표적인 함수는

sin
cos
tan

이다.

직각삼각형에서는

sinθ = 높이 / 빗변
cosθ = 밑변 / 빗변
tanθ = 높이 / 밑변

의 관계가 있다.

게임에서는 방향과 회전을 계산할 때 많이 사용한다.

예를 들어 각도 θ를 방향 벡터로 바꾸면

direction = (cosθ, sinθ)

형태로 표현할 수 있다.

2D 캐릭터가 특정 각도를 바라보도록 만들거나 원형으로 움직이는 오브젝트를 구현할 때 사용할 수 있다.

라디안

컴퓨터 그래픽스와 게임 엔진에서는 각도를 라디안으로 처리하는 경우가 많다.

관계는 다음과 같다.

180° = π rad
360° = 2π rad

변환은

radian = degree * π / 180
degree = radian * 180 / π

이다.

각도 계산을 할 때 도(degree)와 라디안(radian)을 혼동하면 이상한 결과가 나온다.

Atan2

2D에서 두 점 사이의 각도를 구할 때 atan2가 매우 유용하다.

angle = atan2(y, x)

두 위치가 있다면

direction = target - current
angle = atan2(direction.y, direction.x)

으로 대상의 방향을 각도로 변환할 수 있다.

단순히 atan(y / x)를 사용하는 것보다 atan2가 좋은 이유는 x의 부호까지 고려해서 사분면을 올바르게 처리하기 때문이다.

회전

3차원 게임에서 회전은 단순히 x, y, z에 각도를 저장하는 문제로 끝나지 않는다.

대표적인 표현 방법은

Euler Angle
Quaternion
Rotation Matrix

이다.

Euler Angle은 직관적이지만 여러 축의 회전이 겹치면서 Gimbal Lock 문제가 발생할 수 있다.

Quaternion은 3차원 회전을 표현하기 위해 게임 엔진에서 널리 사용된다.

Unity에서도

Quaternion

을 사용한다.

실무에서는 Euler Angle을 편집이나 UI에서 사용하고 실제 회전 계산은 Quaternion을 사용하는 경우가 많다.

행렬

행렬은 좌표 변환을 표현하는 핵심 도구다.

대표적인 변환은

Translation
Rotation
Scale

이다.

게임 그래픽스에서는 이들을 행렬로 표현하고 결합한다.

Local -> World -> View -> Projection

대표적으로 4×4 행렬을 사용한다.

행렬 곱셈은 순서가 중요하다.

AB != BA

일반적으로 교환법칙이 성립하지 않는다.

따라서

Scale -> Rotate -> Translate

Translate -> Rotate -> Scale

은 서로 다른 결과를 만든다.

보간

보간(Interpolation)은 두 값 사이의 값을 계산하는 것이다.

가장 기본적인 것이 선형 보간이다.

Lerp(a, b, t) = a + (b - a)t

여기서

t = 0 -> a
t = 1 -> b

이다.

예를 들어

Lerp(0, 100, 0.5) = 50

이다.

게임에서는

위치 이동
색상 변화
UI 애니메이션
카메라 이동
값의 부드러운 변화

등에 사용한다.

단, Lerp를 매 프레임

position = Lerp(position, target, 0.1)

처럼 사용하는 것은 일반적인 시간 기반 보간과는 다르다.

이 경우 매 프레임 현재 위치에서 목표 위치의 10%만 이동하기 때문에 목표에 지수적으로 접근한다.

SmoothStep

선형 보간은 일정한 속도로 변화한다.

더 부드러운 시작과 끝을 원한다면 SmoothStep 같은 함수를 사용할 수 있다.

대표적인 형태는

f(t) = 3t² - 2t³

이다.

t0에서 1로 변하면서 시작과 끝의 변화량이 부드럽게 줄어든다.

게임에서는 애니메이션, 카메라, UI 등의 자연스러운 움직임을 만드는 데 사용할 수 있다.

거리

두 점 사이의 거리는 벡터의 크기로 계산할 수 있다.

distance = |A - B|

게임에서 거리 계산은 매우 빈번하다.

공격 사거리
적 탐지
아이템 획득 범위
AI 감지
LOD
오디오 감쇠
파티클 활성화 범위

성능이 중요한 경우에는 실제 거리보다 제곱 거리를 비교하는 것이 좋다.

(A - B).sqrMagnitude < range * range

충돌 수학

충돌 검사는 게임에서 핵심적인 수학 문제다.

대표적인 충돌 형태는

Point
Circle
Sphere
AABB
OBB
Capsule
Plane
Ray

등이다.

2D 원 두 개의 충돌은 간단하다.

중심 간 거리가 두 반지름의 합보다 작거나 같으면 충돌한다.

|A - B| <= r1 + r2

제곱근을 제거하면

|A - B|² <= (r1 + r2)²

로 계산할 수 있다.

AABB

AABB는 Axis-Aligned Bounding Box의 약자다.

축에 맞춰진 박스 형태의 충돌 영역이다.

2D에서 두 AABB가 겹치는지 검사하려면 각 축에서 겹치는지를 확인한다.

A.left   <= B.right
A.right  >= B.left
A.bottom <= B.top
A.top    >= B.bottom

두 축 모두 겹치면 충돌한다.

AABB는 계산이 단순하기 때문에 Broad Phase 충돌 검사나 간단한 공간 분할 등에 사용하기 좋다.

Ray

Ray는 시작점과 방향으로 정의되는 반직선이다.

Ray = Origin + Direction * t

여기서

t >= 0

이다.

게임에서는

Raycast
총알 판정
시야 검사
마우스 클릭 위치
바닥 탐색
Line of Sight

등에 사용한다.

예를 들어 플레이어가 바라보는 방향으로 Ray를 발사해서 적과 충돌했는지 확인할 수 있다.

평면

3차원 공간의 평면은 일반적으로 다음과 같이 표현한다.

ax + by + cz + d = 0

여기서

n = (a, b, c)

는 평면의 법선 벡터다.

점과 평면의 관계를 계산하거나 Ray와 평면의 교차점을 계산할 때 사용한다.

게임 그래픽스에서는 클리핑, 충돌, 카메라 Frustum 등과 연결된다.

물리

게임 물리는 뉴턴 역학을 기반으로 한다.

기본적인 운동 방정식은

F = ma

이다.

힘이 작용하면 가속도가 발생한다.

a = F / m

그리고 가속도를 시간에 따라 적분하면 속도가 되고, 속도를 적분하면 위치가 된다.

Force -> Acceleration -> Velocity -> Position

간단한 수치 적분에서는

velocity += acceleration * deltaTime
position += velocity * deltaTime

같은 형태가 사용된다.

중력

중력도 가속도다.

지구 표면 근처에서는 대략

g = 9.81 m/s²

를 사용한다.

게임에서는

velocity.y += gravity * deltaTime

와 같은 형태로 구현할 수 있다.

점프 역시 초기 속도와 중력의 관계로 이해할 수 있다.

v(t) = v0 + gt
x(t) = x0 + v0t + 1/2 gt²

운동량

운동량은

p = mv

이다.

질량이 크거나 속도가 빠를수록 운동량이 커진다.

충돌 시스템이나 물리 시뮬레이션에서 중요한 개념이다.

에너지

운동 에너지는

E = 1/2 mv²

이다.

속도가 2배가 되면 운동 에너지는 4배가 된다.

물리 기반 게임에서 충돌이나 폭발, 발사체 등의 동작을 이해할 때 유용하다.

확률

게임에서도 확률은 많이 사용된다.

Critical Hit
Loot
Drop Rate
Proc
Random Spawn
AI 행동

등이 대표적이다.

예를 들어 아이템 드랍 확률이 10%라면 단순하게

Random() < 0.1

형태로 구현할 수 있다.

하지만 확률과 실제 장기적인 체감은 다를 수 있다.

10% 확률이라고 해서 10번 시도하면 반드시 한 번 나오는 것은 아니다.

가중치 랜덤

여러 아이템 중 하나를 선택할 때는 가중치를 사용할 수 있다.

A = 60
B = 30
C = 10

전체 가중치는

100

이므로

A -> 60%
B -> 30%
C -> 10%

의 확률로 선택할 수 있다.

실제 게임의 Loot Table에서 자주 사용하는 방식이다.

시간과 프레임

게임에서 시간은 수학적으로 중요하다.

프레임마다 일정한 양을 움직이면 프레임 레이트에 따라 속도가 달라진다.

잘못된 방식은

position += speed

이다.

프레임마다 한 번씩 실행되기 때문이다.

일반적으로는

position += speed * deltaTime

처럼 시간 간격을 곱한다.

그러면 FPS가 달라져도 초당 이동량이 일정하게 유지된다.

각속도

일반적인 속도가 위치의 변화율이라면 각속도는 회전의 변화율이다.

velocity -> position 변화
angular velocity -> rotation 변화

회전에서도 시간 간격을 고려해야 한다.

rotation += angularSpeed * deltaTime

물리 엔진에서는 토크와 관성 모멘트까지 연결된다.

게임 수학의 전체 구조

게임에서 사용하는 수학은 서로 따로 존재하지 않는다.

하나의 문제를 해결하기 위해 여러 개념을 같이 사용한다.

예를 들어 적이 플레이어를 향해 이동하는 경우

Player Position -> Target Direction -> Normalize -> Velocity -> DeltaTime -> Position

시야 판정이라면

Forward -> Direction To Target -> Normalize -> Dot Product -> FOV Check

발사체 충돌이라면

Position -> Direction -> Ray -> Intersection -> Collision

카메라와 그래픽스라면

Local Position -> Transform Matrix -> World Space -> View Matrix -> Projection Matrix -> Screen

물리 기반 이동이라면

Force -> Acceleration -> Velocity -> DeltaTime -> Position

처럼 연결된다.

핵심 정리

게임 수학에서 우선순위를 잡는다면 다음 순서가 좋다.

좌표계
-> 벡터
-> 벡터 연산
-> 내적
-> 외적
-> 삼각함수
-> 행렬
-> 회전
-> Quaternion
-> 보간
-> 거리
-> 충돌
-> Ray
-> 물리
-> 확률

이 중에서도 가장 먼저 제대로 이해해야 하는 것은 벡터다.

벡터를 이해하면 위치, 방향, 속도, 거리, 내적, 외적이 연결된다.

그다음 행렬과 Quaternion을 이해하면 3D 공간에서의 이동과 회전이 연결되고, 충돌과 물리를 이해하면 게임 오브젝트의 상호작용을 수학적으로 다룰 수 있게 된다.

결국 게임 수학의 핵심은 공식을 많이 외우는 것이 아니다.

문제
-> 공간에서 무엇을 표현해야 하는가
-> 어떤 수학적 값으로 표현할 것인가
-> 어떤 연산이 필요한가
-> 결과를 게임 로직에 어떻게 사용할 것인가

이 과정을 반복해서 익히는 것이 핵심이다.

게임 개발에서 수학은 목적이 아니라 공간과 움직임을 계산하기 위한 도구다.