게임을 만들다 보면, 포물선을 구현할 일이 참 많다.

물건을 던진다거나, 포탄을 쏜다거나 등등 현실의 물리법칙에 기반한 포물선 운동을 구현해야 할 상황이 많은데 그 공식에 대해 정리해보고자 한다.

 

처음에 물체를 던지면, 어떠한 방향으로 힘을 가하게 될 것이다.

이 때, 힘이 가해지는 방향은 벡터로 표현할 수 있고, 해당 벡터의 길이로 가해진 힘을 구할 수 있을 것이다.

 

이렇게, 최초에 가해진 힘과 방향을 벡터 v로 표시할 수 있을 것이다.

이 벡터 v와 x축과의 사잇각을 theta라고 해보자.

 

그렇다면, 벡터 v를 아래와 같이 표현할 수 있다.

$$\vec{v} = \left (\left | v \right | * cos(\theta), \left | v \right | * sin(\theta)\right )$$

 

이 방향으로 계속 등속 운동을 한다고 가정한다면, t초 후의 벡터는 아래와 같을 것이다.

$$\vec{v} = \left (\left | v \right | * cos(\theta) * t, \left | v \right | * sin(\theta) * t\right )$$

 

하지만, 포물선 운동의 경우 x축에 대해선 등속운동을 진행하지만 y축의 경우엔 중력의 존재떄문에 등속운동을 진행할 수가 없다. 동일한 방향으로 직선 운동을 하되, 가속도의 영향을 계속 받기 때문에 y축의 경우엔 등가속도 직선 운동을 한다고 할 수 있다. 그러므로, 위의 수식의 x값은 맞다고 할 수 있지만, y값에 대해선 틀력다고 할 수 있다.

 

y축은 등가속도 직선 운동을 하기 때문에, 등가속도 직선 운동의 공식을 사용해야 한다.

 

등가속도 직선 운동에서 변위를 구하는 공식은 아래와 같다.

$$s =  v * t \; + \frac{1}{2} * a * t^{2}$$

 

벡터의 속력은 벡터가 최초에 가지고 있던 원소의 값이라고 할 수 있다.  (처음에 가한 힘)

가속도는 -9.8이며, 시간은 t가 될 것이다.

 

공식에 대입하면, t초 후의 변위는 아래와 같다.

$$s =   \left | v \right | * sin(\theta) * t \; - \frac{1}{2} * 9.8 * t^{2}$$

 

즉, 포물선 운동을 (0, 0)에서 시작했다고 한다면, t초 후의 물체의 좌표는 아래와 같아지는 것이다.

 

$$(x, y) =(\left | v \right | * cos(\theta) * t ,\ \left | v \right | * sin(\theta) * t \; - \frac{1}{2} * 9.8 * t^{2})$$

 

현실에선 공기저항과 같은 마찰의 존재때문에, 위의 공식이 정확히 성립하지는 않는다.

하지만, 게임에서 포물선다운 포물선을 그리기에는 충분할 것이다.

 

 

 

 

게임 프로그래밍에서 벡터는 너무나도 많이 사용되고 중요한 존재이다.

그런 벡터를 사용한 여러 연산 중 특히나 자주 쓰이고 중요하게 사용되는 두 가지의 연산이 있다.

바로 내적과 외적이다.

 

이번 게시글에선 내적을 알아볼 것이다.

 

벡터의 내적

벡터의 내적은 아래와 같이 점 기호로 표현한다.

$\vec{a} \cdot \vec{b}$

 

내적은 기본적으로, 두 벡터가 가지고 있는 각 원소의 곱의 합이다.

두 벡터를 아래와 같이 정의해보자.

$\vec{a} = \left \{ a_1, b_1, c_1 \right \}, \vec{b} = \left \{ a_2, b_2, c_2 \right \}$

 

두 벡터를 내적하게 되면 아래와 같다.

$\vec{a} \cdot \vec{b} = a_1 \times a_2\, + \,b_1 \times b_2 \,+\, c_1 \times c_2$

 

내적의 특징은 결과값이 스칼라로 나온다는 것이다.

더보기

벡터 : 방향, 크기를 모두 가지고 있다.

ex) {1, 3} , {2, 5, 7}, {5, -1} ....

 

스칼라 : 크기만 가지고 있다.

ex) 3, 6, 8.4, 1.6 .... 

내적 연산에는 몇 가지 성질이 있다.

 

1. 교환법칙이 성립한다.

$\vec{a} \cdot \vec{b} = \vec{b} \cdot \vec{a}$

 

2.분배법칙이 성립한다.

$\vec{a} \cdot \left ( \vec{b}\,+\, \vec{c} \right ) = \vec{a}\cdot \vec{b} + \vec{a}\cdot \vec{c}$

 

3. 영벡터와의 내적은 결과값이 반드시 0이다.

$\vec{0}\cdot \vec{a} = 0$

 

4. 같은 벡터를 내적하면, 해당 벡터 길이를 제곱한 값이 나온다.

$\vec{a}\cdot \vec{a} = \left | a \right |^2$

 

이 외에도 여러 성질이 있지만, 일단 여기까지만 알아보도록 하자.

 

내적에서 중요한 것은 아래의 공식이다.

$\vec{a}\cdot \vec{b} = | \vec{a} |\times| \vec{b}|  \times cos(\theta)$

 

두 벡터의 내적은 두 벡터의 길이와 두 벡터의 사잇각(theta)의 cos값을 곱한 것과 같다.

 

아래는 증명과정이다. 

더보기

 

제 2 코사인 법칙을 활용하면, 아래 공식을 유도할 수 있다.

$\vec{a} - \vec{b}|^2 = \left | \vec{a} \right |^{2} + |\vec{b}|^{2} - 2|a||b|cos(\theta )$

 

위에서 설명한 내적의 성질을 활용하면, 좌항에 대해 아래와 같은 식을 유도할 수 있다.

$|\vec{a} - \vec{b}|^2 = |\vec{a} - \vec{b}|\cdot |\vec{a} - \vec{b}|$

$|\vec{a} - \vec{b}|\cdot |\vec{a} - \vec{b}| = |\vec{a}|^2 -2\times \vec{a}\cdot \vec{b} + |\vec{b}|^2$

 

이제, 두 식을 합쳐보자.

$ |\vec{a}|^2 -2\times \vec{a}\cdot \vec{b} + |\vec{b}|^2 = \left | \vec{a} \right |^{2} + |\vec{b}|^{2} - 2|a||b|cos(\theta )$

 

식을 정리하면 아래와 같다.

$\vec{a}\cdot \vec{b}= |a||b|cos(\theta )$

위의 공식을 활용하면 두 벡터의 내적을 이용해, 두 벡터 사이의 각도를 구할 수 있다는 것이다.

게임 수학에서 두 벡터 사이의 각도를 구해야하는 일은 상당히 많다.

 

예를 들면, 빛의 방향벡터와 물체의 법선벡터의 사잇각을 이용해 굴절, 반사 등의 효과를 적용하기도 하고, 플레이어와 적의 위치관계를 알아내는데에도 사용된다.

 

게임 내에서 여러 상호작용을 보면 물체간의 위치관계가 중요한 상황이 참 많다.

 

예를 들면, 몬스터의 시야 범위에 현재 들어와 있는가? (잠입 액션, 정찰 등)

내가 몹의 뒤에 서있는가? (백어택, 암살 등)

 

그렇다면 이런 위치관계를 어떻게 구해야 할까?

게임 프로그래밍에선 일반적으로 벡터의 내적과 외적을 이용하여 구한다.

 

이 내용을 이해하려면 벡터에 대한 기본적인 개념이 있어야 한다.

벡터에 대한 지식이 없다면 찾아보고 나서 읽는 것을 추천한다.

 

 

먼저 이런 위치 관계예 놓여있다고 하자.

좌표상으로만 보면, 몹은 나의 위에있고 오른쪽에 있는 것이다.

하지만, 이건 절대적인 좌표의 기준일 뿐이고 만약 내가 왼쪽을 보고있다면?

 

아래 사진처럼 왼쪽을 바라보며 서있다면, 몹은 나의 뒤에 존재하게 되고, 나의 오른쪽에 존재하게 된다.

 

즉, 몹과 나의 방향 관계를  정확히 파악하려면 먼저 바라보고 있는 방향에 대한 벡터가 필요하다.

방향 벡터를 구하는 것은 어렵지 않다.

 

먼저, 플레이어의 위치, 회전관계를 전혀 건드리지 않았을 때 최초에 바라보는 방향이 필요하다.

 

아마 일반적인 상태에선 최초에 바라보는 방향에 대한 벡터는 (0, 0, 1)일 것이다.

(매쉬에 따라 다를 수 있고, 엔진 환경에 따라 다를 수 있다. 다만, 최초에 바라보고 있는 방향 벡터라는 것만 알면 된다.)

 

이런 상황에서 플레이어가 x축으로 60도만큼 회전했다면?

바라보는 방향에 대한 벡터 또한 x축으로 60도만큼 같이 회전할 것이다.

 

즉, 플레이어의 현재 회전값만큼 항상 함께 회전하기 때문에 (0,0,1)벡터를 플레이어의 회전 값만큼 회전시키면

그게 현재 플레이어의 정면 벡터인 것이다.

 

정면에 대한 벡터는 이렇게 구해보았다. 그렇다면 다음은 무엇을 해야 할 까.

 

 

아래 사진을 보자. 보라색 벡터는 플레이어로부터 몹을 향하는 벡터이다.

 

정면을 향하는 벡터와 몹을 향하는 벡터 사이의 각 (Theta)가 90도보다 크다면, 몹은 뒤에 있다고 할 수 있지 않을까?

반대로, Theta가 90도 보다 작다면, 몹이 내 앞에 있다고 할 수 있지 않을까?

 

그렇다. 노란색 벡터(정면에 대한 벡터)와 보라색 벡터(몹을 향한 벡터) 사이의 각도를 구하면 몹이 앞에 있는지 뒤에 있는 지를 판별할 수 있다.

 

일단, 각도를 구하기 전에 보라색 벡터(몹을 향한 벡터)를 구해보자.

이건 간단하게 구할 수 있다.

 

(몹의 위치 좌표 - 나의 위치 좌표)가 보라색 벡터이다.

벡터의 뺄셈 연산을 알고있다면 이해하는 것에 무리는 없을 것이다.

 

이렇게, 정면 벡터와 몹을 향한 벡터를 구했다면 두 벡터 사이의 각을 구하면 된다.

 

그런데, 그 전에 한가지 알고 넘어가야 하는 사실이 있다.

바로 몹과 나의 y축 좌표(위, 아래)를 고려해야 하는가? 에 대한 문제이다.

 

나의 정면 벡터는 정상적으로 서있다면, 항상 지면에 수평이다.

하지만, 나와 몹의 높이 차이가 존재한다면 몹을 향한 벡터는 지면에 수평이지 않을 수 있다.

즉 두 벡터 사이의 각도가 우리가 의도하는 것과는 다르게 나올 수 있다는 것이다.

 

단순히 몹이 내 앞에 있는가? 뒤에 있는가? 만을 고려한다면 y값의 차이는 계산에 영향을 미쳐선 안된다.

그렇기 때문에, 정면 벡터의 y값을 0으로, 몹을 향한 벡터의 y값을 0으로 하여 x와 z만을 가지고 계산하는 것이 일반적으로 옳다.

 

그렇게 y값을 제거한 뒤, 두 벡터를 구했다면 이제 각도만 구하면 된다.

 

각도는 벡터의 내적을 이용하여 구하면 된다.

A · B =  벡터A의 길이 * 벡터B의 길이 * Cos(Theta)이다.

 

여기서 길이의 곱만 제거한다면 Cos(Theta)만 남을 것이다.

그렇기 때문에, 두 벡터를 내적하기 이전에 먼저 정규화(노말라이즈)를 해주어야 한다.

정규화란 벡터의 길이를 1으로 맞추는 작업이며 벡터의 크기의 영향을 제거하고 방향만을 가지고 계산을 하기 위한 연산이다.

 

정규화는 어렵지 않다.

벡터의 x,y,z 성분을 벡터의 크기로 나누어 주면 끝이다.

벡터의 크기가 L이라면, (x/L, y/L, z/L)이 정규화된 벡터이다.

 

이렇게, 정면 벡터와 몹을 바라보는 두 벡터를 정규화 하였다면, 두 벡터를 내적해주자.

내적은 각 성분을 모두 곱해주면 된다.

 

(X1, Y1, Z1), (X2, Y2, Z2) 두 벡터가 있다면, (X1 * X2 + Y1 * Y2 + Z1 * Z2)가 두 벡터의 내적 값이다.

 

이렇게, 정면 벡터와 몹을 바라보는 두 벡터를 내적하였다면, 그 값은 Cos(Theta)일 것이다.

 

A · B =   (X1 * X2 + Y1 * Y2 + Z1 * Z2)  = 벡터A의 길이 * 벡터B의 길이 * Cos(Theta) 이고, 두 벡터의 길이는 1이므로

A · B =   (X1 * X2 + Y1 * Y2 + Z1 * Z2)  = Cos(Theta)

 

여기서, acos함수를 이용하여 Theta를 정확히 구할 수도 있지만 사실 불필요한 연산이다.

Cos함수의 경우 각도에 따른 값의 범위가 명확히 정해져 있다.

 

0 <= Theta < 90 이라면, 0 < Cos(Theta) <= 1 이고

90 <= Theta < 180 이라면, -1 < Cos(Theta) <= 0 이 될 것이다.

 

즉, 두 벡터를 내적한 값이 0과 1 사이라면, 몹은 내 앞에 있는 것이며

-1과 0 사이라면 나의 뒤에 있는 것이라고 판별할 수 있다.

 

하지만, 이렇게 내적으로 몹과 나의 위치관계를 구하게 되면 한 가지 문제가 있다.

내적은 0~180도 범위에서만 작동한다.

저렇게 몹이 반대편에 위치한다고 하더라도 값은 똑같이 나온다.

즉, 앞 뒤는 구별할 수 있어도 왼쪽 오른쪽은 구별할 수 없다는 뜻이다.

 

그렇다면 왼쪽 오른쪽은 어떻게 구할까?

 

간단하다. 정면벡터와 몹을 향하는 방향 두 벡터를 외적해보면 된다.

 

두 벡터의 y값을 제거하고 지면에 수평인 벡터로 만들어 준다.

그리고 두 벡터를 외적하면 지면에 수직힌 벡터가 나올 것이다.

 

두 벡터의 위치 관계에 따라, 지면 위로 솟아나는 방향일 수도 있도, 지면 안쪽으로 들어가는 방향일 수도 있다.

 

무조건, 정면 벡터를 앞에 두고 ( 정면 벡터 x 몹을 향한 벡터 ) 이렇게 외적을 해보자.

오른손 법칙에 의해, 몹을 향한 벡터가 정면 벡터보다 왼쪽을 향하고 있다면 위로 솟아나는 벡터가 나올 것이고

몹을 향한 벡터가 정면 벡터보다 오른쪽을 향하고 있다면 아래로 들어가는 벡터가 나올 것이다.

 

즉, 두 벡터를 외적한 벡터의 y값이 양수이면 왼쪽, 음수이면 오른쪽 이라고 판별할 수 있다.

 

https://snowfleur.tistory.com/98 에서 퍼온 사진

 

그림에도 나와 있듯이, 정면 벡터(u) 를 기준으로 했을 때, 몹을 향한 벡터(v)가 왼쪽에 위치한다면 외적한 벡터는 위를 향할 것이다.

당연히, 오른쪽에 있다면 외적한 벡터는 아래를 향할 것이다.

다만 아주 중요한 점이 있다

위에선 오른손을 기준으로 외적의 뱡향을 설명했지만, 항상 이 기준이 오른손이 되는 것이 아니다. 좌표계를 어떻게 정의하느냐에 따라 달라질 수 있다. DirectX, Unreal Enigne, Unity에선 왼손을 기준으로 계산 해야한다!반면, OpenGL, Vulkan 등에선 오른손을 기준으로 계산 해야한다!

 

이렇게, 내적과 외적을 활용해서 다른 오브젝트와 나의 위치 관계를 구해보았다.

게임을 프로그래밍하다 보면, 내적과 외적은 정말 활용할 곳이 너무너무 많다.

반드시 알아야만 한다!

'게임수학' 카테고리의 다른 글

게임 수학 - 포물선 운동  (0) 2024.05.09
게임 수학 - 벡터의 내적 (Dot Product)  (0) 2024.04.27

+ Recent posts