Win32 API 기초 : 기초 수학 (2)
초당 600의 거리를 이동하는 발사체를 발사하고 싶을 때,
삼각함수를 통해서 원하는 각도만 신경 써 주면
속도가 알아서 설정되어 손쉽게 초당 600의 거리를
이동하는 발사체를 구현할 수 있었습니다.
우선 초당 1만큼, 45도 방향으로 발사체를 이동시키는 것을
기준으로 x 좌푯값과 y 좌푯값이 몇이어야 하는가를 파악했죠.
이때 x 좌푯값은 밑변으로 cosθ,
y 좌푯값은 높이로 sinθ였습니다.
그럼 결국 초당 1만큼, 45도 방향으로 발사체를 이동시키려면
x 좌푯값은 1 * cos45도 * DT, y 좌푯값은 1 * sin45도 * DT로
하면 되고 이를 응용해서 초당 600만큼, 45도 방향으로
발사체를 이동시키려면 x 좌푯값은 600 * cos45도 * DT,
y 좌푯값은 600 * sin45도 * DT로 설정하면 되는 거죠.
( 여기서 DT는 DeltaTime으로 한 프레임이 실행되는 동안 걸리는 시간입니다. )
개념은 같지만 Vector를 이용하면
각도를 신경 쓸 필요 없이 발사체를
원하는 방향으로 이동시킬 수 있습니다.
( 자료구조 Vector가 아닙니다. )
x 좌푯값으로 3만큼,
y 좌푯값으로 2만큼 이동한 지점에
점을 찍어 직각삼각형이 형성되었습니다.
이때 tanθ = 높이 / 밑변 이므로 대입하면
tanθ = 2 / 3 이죠.
이를 통해 각도( 방향 )를 구할 수 있지만
그럴 필요 없이 ( 3 , 2 )라는 좌푯값을
통해 각도, 즉 방향이 만들어지고 있으므로
좌푯값 자체를 방향으로 생각하면 되는 것이죠.
세타 각도 방향으로 x축으로 3, y축으로 2 이동한
시점에 점을 찍어 그곳을 도착 지점으로 하고 있으니
위 직각삼각형의 빗변이 곧 크기를 의미하겠죠?
피타고라스 정리에 의해
3^2 + 2^2 = 빗변^2 이고 계산하면,
9 + 4 + 빗변^2 이므로
√13 = 빗변 이라고 할 수 있습니다.
즉 위 직각삼각형 그림을
발사체의 초당 이동 거리와 방향로 생각해 보면
발사체를 ( 3 , 2 ) 방향으로 발사했을 때
1초 후에는 ( 3 , 2 ) 지점에 도착할 것이며
이때 이동한 거리를 측정해 보면 √13인 것이죠.
기존에는 각도로 설정하여
발사체가 원하는 방향으로 날아가도록 설정했습니다.
45도로 설정을 했었죠?
세타가 45도인 직각삼각형은 곧,
직각 이등변 삼각형이죠?
밑변과 높이의 길이가 같음을 의미하죠.
즉 45도 방향으로 발사체를 발사하면
x 좌푯값과 y 좌푯값의 비율은
항상 1 : 1로 유지됩니다.
바꾸어 말하면,
두 좌푯값이 같다는 것은
각도가 45도임을 의미하는 것이죠.
이를 이용하여 발사체의 방향을
( 1, 1 )로 설정해 주면 발사체가 알아서
45도 방향으로 이동할 것입니다.
이렇게 방향을 설정해 주었습니다.
그리고 이때 빗변의 길이는 √2이므로
Vector ( 1, 1 )이 갖는 크기는 √2입니다.
근데 우리가 필요한 것은 크기를 의미하는
빗변의 길이, 즉 발사체의 이동 거리가
1일 때입니다.
이동 거리가 1일 때를 기준으로 하는 이유는
해당 경우만 파악해 놓으면, 다른 경우도
모두 계산할 수 있게 되기 때문입니다.
만약 발사체의 이동 거리를 2로 설정하고 싶다면
단순히 발사체의 이동 거리인 2를
모든 변에 곱해주기만 하면 되죠.
( 두 직각삼각형이 '닮음'이기 때문. )
만약 발사체의 이동 거리를 600으로,
방향 정보를 ( 1, 1 )이라고 했다고 합시다.
그럼 x축으로 600, y축으로 600을 가는 것일까요?
아니죠?
발사체의 이동 거리를 1로 하였을 때,
방향 정보가 어떻게 나와야 하는지 알아야
발사체의 이동 거리를 600으로 하였을 때,
방향 정보가 어떻게 나와야 하는지도 알겠죠.
발사체의 이동 거리를 1로 하였을 때,
방향 정보를 ( 1, 1 )이라고 하면
두 좌푯값의 비율이 같으므로 45도 방향이 될 것이고
결국 1 : 1인 비율을 유지하면서 빗변의 길이가
1이 되려면 x, y 좌푯값은 ( cos45도, sin45도 )로
다시 표현할 수 있죠.
( 빗변이 1이므로 x 좌푯값은 cosθ,
y 좌푯값은 sinθ 그 자체가 되죠. )
즉 x, y 좌푯값은 빗변이 1일 때 기준,
x축 성분과 y축 성분을 의미하는 것이죠.
여기서 빗변의 길이를 600으로 늘리고 싶다면
x축에 600을 곱하고 y축에도 600을 곱하면 되겠죠?
그럼 빗변의 길이가 1이고 밑변( x 좌푯값 )과
높이( y 좌푯값 )의 길이가 1 : 1 비율인 직각삼각형과
닮은, 빗변의 길이가 600이며 밑변( x 좌푯값 )과
높이( y 좌푯값 )의 길이가 1 : 1 비율인
직각삼각형인 형성될 겁니다.
이를 CMissile 클래스에 정의를 해 보자면
Vector를 저장할 멤버 m_vDir를 선언하고
( 1, 1 )로 초기화를 진행합니다.
근데 이대로라면
x 좌푯값과 y 좌푯값이 둘 다
1이므로 빗변의 길이가 √2가 됩니다.
이것을 x 좌푯값과 y 좌푯값의 1 : 1 비율을
유지하면서 빗변의 길이가 1일 때,
x 좌푯값과 y 좌푯값이 몇이냐를
파악하는 것이 바로
Vector의 정규화( Normalize ) 라고 합니다.
위 직각 이등변 삼각형을 Normalize하면
두 변의 길이가 같으므로 45도일 것이고,
여기서 빗변의 길이를 1로 줄이게 되면
x 좌푯값은 cosθ, y 좌푯값은 sinθ가 됩니다.
근데 아래 그림과 같이
발사체의 초당 이동 거리를 600으로 하고
x 좌푯값과 y 좌푯값이 각각 3 : 7 비율을
갖고 있다고 가정해 봅시다.
그러면 발사체는 위 그림과 같은 각도로
초당 600 거리를 이동하게 될 겁니다.
저런 형태의 직각삼각형을 Normalize하려면,
즉 빗변을 1로 만들었을 때의 x, y 좌푯값을
구하려면 각도를 구한 cosθ와 sinθ를 통해
값을 구해야 하므로 번거로울 겁니다.
이를 쉽게 하기 위해 정규화 공식이 있습니다.
각 좌푯값을 빗변의 길이로 나누면 됩니다.
그럼 3 : 7 비율을 유지한 채
빗변의 길이의 길이를 1로 줄인 상태의
x 좌푯값과 y 좌푯값을 알 수 있죠.
피타고라스 정리에 의해 빗변의 길이는
3^2 + 7^2 = 빗변^2 이므로,
가 빗변의 길이가 됩니다.
이후 각 좌푯값을
빗변의 길이로 나눕니다.
바로 이 좌푯값들이
빗변의 길이가 1일 때의 x, y 좌푯값인 겁니다.
그럼 확인해 보겠습니다.
위의 두 좌푯값이 정말 빗변의 길이가
1일 때의 좌푯값이라면,
피타고라스 정리에 의해
이 식이 성립해야 될 겁니다.
계산해 보면
이 상태에서 약분하면
정말 공식이 성립하는 것을 확인할 수 있습니다.
이를 CMissile 클래스에 적용을 시키겠습니다.
생성자를 통해 멤버를 초기화할 때
방향을 나타내는 멤버 m_vDir의
멤버 함수 Normalize를 호출하게 됩니다.
Vec2 자료형은
이제 Length 함수와 Normalize 함수를 포함하고 있습니다.
Length 함수를 통해 인자로 받은 두 개의 좌푯값을
통해 빗변의 길이를 구합니다.
그리고 Normalize 함수에서 Length 함수를 통해
구한 빗변의 길이를 지역 변수 fLen에 저장하고
x 좌푯값과 y 좌푯값을 빗변으로 나누어
이를 다시 x, y 좌표에 대입합니다.
그럼 정규화가 진행되어 두 좌푯값은
빗변의 길이가 1일 때의 좌푯값을 저장하게 되겠죠.
이렇게 형성된 Vec2를 this 포인터를 통해 반환합니다.
18번째 줄에 있는 assert 매크로는
괄호 안의 조건이 true일 때 그냥 넘어가지만,
false가 되면 경고 창을 띄우며 프로그램을 종료합니다.
즉 fLen에 저장된 빗변의 길이가
0이 아닐 때는 넘어가지만,
0이면 두 좌푯값을 0으로 나누는
동작을 수행하지 않도록 방지하기 위해
미리 assert 매크로를 통해 프로그램을
종료시키는 겁니다.
다시 CMissile 클래스의 소스 파일로 돌아와서
이렇게 정규화된 Vec2형 멤버 m_vDir의
x, y값들과 발사체의 이동 거리와
초당 프레임이 실행되는 동안 걸리는 시간인 fDT를 곱해
발사체의 좌푯값에 대입함으로써 발사체의 방향과
속도가 정해지는 거죠.
이제 CPlayer 클래스의 소스 파일로 가서
발사체를 생성하는 CreateMissile 함수를 봅시다.
53번째 줄에서 SetDir 함수를 통해
발사체의 방향을 설정해 주고 있네요.
플레이어 기준 x축 -7, y축으로 -3의
방향으로 이동하게 될 겁니다.
이때 윈도우는 위로 갈수록 y축의 값이
마이너스가 되며 아래로 갈수록 플러스가
되므로 위(앞)로 발사체를 이동시키려면
y축의 값을 음수로 주어야 합니다.
CMissile 클래스에 헤더 파일에
SetDir 함수가 정의되어 있습니다.
SetDir 함수는 오버로딩되어,
float형 인자를 받아 각도를 통해
발사체의 방향을 설정할 수도 있고
Vec2형 인자를 통해 방향을
설정해 줄 수도 있습니다.
이 경우 SetDir 함수 내부에서
인자로 받은 값들을
Normalize 함수를 통해
정규화해 줍니다.
이렇게 Vector를 사용하면 좋은 점은
만약 위 그림과 같이 오브젝트를 움직이기 위해
왼쪽 대각선 방향으로 힘을 주었는데,
오른쪽 대각선 방향으로도 힘을 주게 되면
오브젝트에 가해지는 힘이 상쇄되어
직선 방향으로 전진하게 될 겁니다.
이것을 각도로만 계산하게 되면
최종 이동 방향을 계산하는 게 까다로워지는데
Vector는 간단하게 성분끼리 더해 주면 됩니다.
( 1, 1 ) 방향과 ( 1, -1 ) 방향이 있을 때
최종 방향은 두 좌푯값을 더한
( 2, 0 )이 됩니다.
이렇게 Vector끼리의 덧셈, 뺄셈을 통해
진행 방향끼리의 덧셈과 뺄셈이 쉽게 가능하여
손쉽게, 직관적으로 방향 정보를 다룰 수 있게 됩니다.
강의 출처 : https://www.youtube.com/watch?v=tt-_NfRfFAw&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=20