게임을 잘 만들려면 어떡해야 할까? 내 생각에는 세상의 최대한 많은 것들을 아는 것이 정답인 것 같다. 게임을 게임답게 만드는 온갖 낭만을 첨가하기 위해 인문학적 소양을 기르거나, 시스템을 빠른 속도로 기깔나게 개발하기 위한 개발론을 습득하거나.
많은 걸 알면 많은 문제를 쉽게 해결할 수 있다.
이미 널리 알려진 기능들을 모방하는 단계를 넘어, 본인만의 복잡한 기능을 구현할 필요가 생기기 시작하면 코딩 외에 지식들이 절실해진다. 그 예로 독자적인 시뮬레이터를 구현하기 위해 물리 법칙을 구현하며 애를 먹었던 일이 있는데, '버렛 적분'이라는 기가 막힌 수식으로 대부분의 문제를(완벽하진 않지만) 해결해 버린 기적을 경험한 적 있다.
개발자는 많을 것을 알아야 한다. 그게 내 생각이고, 이를 위해 기초부터 다져보려고 한다. 그런 의미로 본 포스팅에서는 게임 수학이라고 불리는 기초적인 개념들을 공부해보려 한다.
Vector
Inmathematicsandphysics, vectoris a term that refers toquantitiesthat cannot be expressed by a single number (ascalar), or to elements of somevector spaces. They have to be expressed by both magnitude and direction.
수학과 물리학에서 벡터는, 스칼라가 아닌 물리량 혹은 벡터 공간의 원소를 의미한다. 공통적으로 크기와 방향으로 표시된다.
- Wikipedia 벡터의 구성
만약 누군가 나한테 벡터가 무엇인지 물어 본다면 뭐라고 답해야 할까? 벡터의 개념을 읇는 것만으론 불충분하다. 내 전공인 소프트웨어학, 혹은 게임 개발에 맞춰 벡터를 설명하는 것이 좋다고 생각한다.
벡터는 여러가지 방향을 구할 수 있는 데, 공이 튕기는 방향이나 병을 따라 이동할 떄의 방향 같은 것도 구할 수 있다. 벡터가 쓰이는 대표적인 사례를 알아보자.
1. 앞? 뒤?
아마테라스
일본 만화 나루토에는 "아마테라스"라는 기술이 존재한다. 눈에 보이는 모든 것들을 태워버리는 가히 사기적인 기술인데, 눈에 안 보이는 것은 태울 수 없다는 단점이 존재한다. 애초에 전부 타는데 뭐가 보이긴 할까?
아무튼, 이 아마테라스를 구현하기 위해선 내 시야에 어떤 물체가 들어오는지 알아야 한다. 이럴 떄 사용하는 것이 벡터의 내적이다.
내적 공식
B를 내가 바라보는 방향, A가 오브젝트의 위치를 가리키는 벡터라고 해보자.
내적은 두 벡터의 크기와 두 벡터 사이 각도의 코사인 값을 곱하여 구한다. 중요한 점은 내적이 사잇각의 코사인 값과 연관된다는 것이다. 코사인 함수의 특성상, 두 벡터가 같은 방향을 향할수록 값이 1에 가까워지고, 반대 방향을 향할수록 -1에 가까운 값을 반환한다.
시야에 적이 들어오는가?
위 그림처럼 플레이어의 시야를 기준으로 다른 오브젝트가 플레이어 앞에 존재하는지 판단할 수 있다. 플레이어의 시야 벡터와, 플레이어 위치에서 다른 오브젝트 위치까지의 벡터를 내적한 다음, 그 값이 양수면 다른 오브젝트가 앞에 있는거다.
사이각 구하기
내적 공식을 이용하면 시야각(Field of View) 내에 오브젝트가 존재하는지 판별할 수 있다. 두 벡터가 단위벡터일 경우, 내적값의 역코사인(arc cosine)을 사용하여 두 벡터 사이의 각도를 라디안 값으로 구할 수 있다. 예를 들어, 플레이어의 시야각이 160도이고, 계산된 사이각이 69도라면, 이는 시야각의 반각(half FOV)인 80도보다 작으므로 오브젝트는 시야 안에 포함된다고 할 수 있다.
응용
FPS 게임의 조준점과 적 사이의 각을 내적을 통해 구한 후 에임 보정.
광원 벡터와 플레이어 시야 벡터 사이의 각을 내적을 통해 구한 후 광원량 조정
사운드가 들리는 위치의 벡터와 플레이어 시야 벡터 사이의 각을 내적을 통해 구한 후 소리 조절
2. 시시포스의 깨달음
시시포스의 형벌
시시포스는 오늘도 돌을 굴린다. 이 끝나지 않는 끔찍한 형벌 속에서 영원토록 돌을 굴리며 시시포스는 아마 벡터에 대해 꺠우쳤을 것이다. 경사 위로 돌을 굴릴 때, 왜 경사와 평행하게 힘을 주는 게 유리한 걸까? 더 나아가 우리는 경사와 평행한 벡터를 구하는 방법까지 생각해야 한다.
경사면에서의 힘
경사면 위 돌에 작용하는 중력은 두 개의 벡터로 나눌 수 있다. 그 중 중요한 벡터는 경사면에 평행한 벡터다. 이 벡터 때문에 돌은 경사면에서 굴러 떨어지는 것이다. 때문에 돌을 올리기 위해선 정확히 반대 방향으로 +a의 힘을 가해야 한다.
근데, 애초에 굴러 떨어지는 방향이나 시시포스가 힘을 가해야 할 방향을 어떻게 구해야 할까?
경사 각도 구하기
경사로의 각도를 구하기 위해 필요한 벡터는 두 가지 법선 벡터이다. 하나는 지면에 평행한 법선 벡터, 다른 하나는 경사면에 수직인 법선 벡터다. 앞서 내적을 통해 두 개 벡터의 사잇각을 구했듯 이번에도 구하면 된다.
유니티나 언리얼이면 각각Vector3.up, FVector::UpVector가 지면에 수직인 벡터에 해당한다.
경사에 수직인 벡터는 유니티의 경우 ContactPoint.normal을 통해 구할 수 있고, 언리얼의 경우 FHitResult::Normal를 통해 구할 수 있다.
유니티나 언리얼이 아니면 각자 환경에 맞는 방법으로 구해야 한다. 보통 외적을 통해 면에 수직인 법선 벡터를 구할 수 있다.
경사로에서 힘을 가해야 할 방향 구하기
지면(평지)과 평행한 벡터와 앞서 구한 경사 각도의 코사인 값을 곱하면 경사로와 평행한 벡터를 구할 수 있다.
여기서 평지화 평행한 벡터는 상황에 따라 직접 계산해줘야 한다. 한 가지 예를 들자면, 오로지 앞으로만 힘을 가하는 공의 경우 Vector3.forward가 지면과 평행한 벡터가 된다. 하지만 만약 공의 이동 방향을 자유로이 변경할 수 있다면, Vector3.forward를 지면에 투영하여 지면과 평행한 벡터를 구해야 한다.
3. 왼쪽? 오른쪽?
오브젝트가 플레이어의 앞, 뒤에 있는지 판단하는 건 내적이었다. 대신, 오브젝트가 플레이어의 좌, 우 어디에 있는지 판단하는 건 외적이다.
외적
내적값은 "스칼라"인 반면, 외적값은 "벡터"다. 따라서 오브젝트가 물체의 좌 우 어디에 있는지 판단하는 기준은 벡터의 한 가지 원소로 판단한다. 지면과 수직인 축의 원소가 해당하는데 유니티의 경우는 Y축 원소, 언리얼은 Z축 원소다. sin 값에서 비롯하는 스칼라 곱에 의해 지면과 수직인 축의 원소가 양수이면 우측, 음수이면 좌측이다. 이제 외적값을 이용해 왼쪽 오른쪽 어디로 회전할지 정하면 된다.
그리고 또 외적은 면의 법선 벡터를 구하는데 사용한다. 이를 통해 위에 시시포스 부분에서 했던 경사로에서의 이동 벡터를 구할 수 있다.
4. 그림자
투영
투영(Project)은 한 벡터를 다른 벡터 위로 끌어내리는 거다. 아주 좋은 예시로 그림자가 있다.
빛의 투영
빛을 벡터라고 생각해보자. 그림자는 위 그림과 같이 생겨날 거다. 이 때, 그림자의 길이는 빛의 벡터를 지면에 투영시킨 것과 같다(사람을 기준으로 거리를 잰다고 했을 경우). 만약 빛이 사람 머리 꼭대기로 바로 꽃힌다면 그림자는 생기지 않는다. 빛과 지면이 이루는 각도가 90도가 되면 cos 값이 0이 되기 때문이다.
충돌 후 이동 처리
투영을 통해 물체가 충돌한 후의 운동도 계산할 수 있다.
번외
C++ std::vector
요 녀석은 처음 봤을 때부터 이름이 왜 vector인지 궁금했다. 알고보니 STL의 설계자인 Alex Stepanov가 동적 배열을 built-in array와 구분 짓기 위해 vector라는 이름을 붙였는데, 안타깝게도 built-in array가 사실은 수학적인 의미의 vector에 더 가깝다는 사실을 뒤늦게 알았다고 한다.
Unity Vector3 [ 자세한 내용 : Scripting ] [ 자세한 내용: 가이드 ]
UnityEngine 라이브러리에 정의된 Vector3는 구조체이며, 값 타입이다. 제공하는 유용한 기능은 다음과 같다.
내적(Dot), 외적(Cross), 투영(Project), 반사(Reflect) 같은 정적 벡터 연산 함수
The operations of addition and scalar multiplication are used in Many diverse contexts in mathematics. Regardless of the context, however, these operations usually obey the same set of algebraic rules. Thus, a general theory of mathematical systems involving addition and scalar multiplication will be applicable to many areas in mathematics. Mathmatical systems of this form are called vector spaces or linear spaces. In this chapter, the definition of a vector space is given and some of the general theory of vector spaces is developed. - Steven J. Leon, "Linear Algebra with Applications - 9th", 128p