본문 바로가기
Win32 API/기초

Win32 API 기초 : Win32API Object (1)

글: 시플마 2024. 7. 6.

CObject 클래스를 통해 사각형 객체를 만들고

화면에 출력하였습니다. 

 

이를 키 입력을 통해 움직일 수 있도록 하였죠.

 

이제는 CObject 클래스를 통해

플레이어 객체를 생성해 줄 자식 클래스를 만들겠습니다.

 

먼저 아래 코드에서 17번째 줄에서 보이듯이

 

update 함수를 순수 가상 함수로 만들어 주었습니다.

 

CObject 클래스를 상속받은 자식 클래스로 만든

오브젝트는 각 유형에 따라 고유의 update 함수를

갖게 할 것입니다. update 함수는 어떤 오브젝트냐에 따라

다르게 작동해야 하니까 말이죠.

 

가상 함수로 정의해 줌으로써

update 함수를 호출하면 부모 클래스의

update 함수가 아닌 자신의 update 함수를

호출할 수 있도록 하였습니다.

 

이 가상 함수를 또 순수 가상 함수로 정의하면서

부모 클래스인 CObject 클래스를 통해 객체를

만드는 일을 방지하고 CObject 클래스의

자식 클래스는 반드시 update 함수를

구현하고 있어야 하도록 했죠.

 

 


 

 

 

Object 필터 안에 Player 필터를 만들고

 

그 안에 CPlayer 클래스를 만들었습니다.

 

해당 클래스는 CObject 클래스를

상속받아 구성하였죠.

 

CPlayer 클래스의 헤더 파일을 보시면

 

update 함수가 있는 것이 보입니다.

 

부모 클래스에 update 함수가 순수 가상 함수로

정의되어 있으므로 자식 클래스에는 반드시

update 함수가 구현되어 있어야 오류가 발생하지 않습니다.

 

 

CPlayer 클래스의 소스 파일을 보시면

 

플레이어의 update 함수는 

키 입력을 통해 오브젝트의 위치를

변경시키는 역할을 하고 있습니다.

 

GetPos 함수를 통해 부모 클래스에 있는

좌푯값을 지역 변수 vPos에 저장하고

키 입력이 있다면 vPos에 저장된 값을

수정한 후 SetPos 함수를 호출하여

수정된 값을 부모 클래스에 있는 멤버에

다시 적용시켜 주고 있습니다.

 

 

이러면 오직 CPlayer 클래스를 통해 만든 객체,

즉 플레이어 객체만이 키 입력을 받고 움직일 수 있는

상태가 될 것입니다.

 

만약 부모 클래스인 CObject 클래스에 

위와 같은 update 함수를 구현하였다면

키 입력이 들어왔을 때 모든 객체가 움직이는

문제가 발생하겠죠.

 

 

 


 

 

이제 몬스터 객체도 만들어 보겠습니다.

 

 

 

아래 사진은 CObject 클래스를 상속 받은

 

CMonster 클래스입니다.

 

멤버로는 몬스터가 움직일 기준점이 되는 

m_vCenterPos와 몬스터의 속도를 저장하는

m_fSpeed, 몬스터가 움직일 최대 거리를 저장하는 

m_fMaxDistance, 몬스터가 움직일 방향을 결정하는

m_iDirection가 있습니다.

 

이 중 m_vCenterPos와 m_fSpeed, m_fMaxDistance 멤버는

해당 멤버의 값을 세팅하고 반환하는 Get, Set 함수가 존재하죠.

 

 

몬스터의 update 함수는

플레이어의 update 함수와 어떻게 다른지

살펴봅시다.

 

CMonster 클래스의 소스 파일입니다.

 

몬스터는 좌우로 왔다 갔다 움직이게 할 겁니다.

 

CMonster 클래스의 생성자를 보시면 몬스터의

초기 속도는 100이며 움직일 거리는 50으로 되어 있으며,

오른쪽부터 움직이도록 설정되어 있습니다.

 

m_iDirection의 값이 1이면

오른쪽 방향을,  -1이면 왼쪽 방향을 의미합니다.

 

 

우선 GetPos를 통해 몬스터의 위치 좌푯값을

지역 변수 vPos에 저장합니다.

 

그리고 매 프레임마다 설정된 속도와 방향, Delta Time을 곱하고

기존의 몬스터의 x 좌푯값과 더하여 이를 다시 

몬스터의 x 좌푯값으로써 저장합니다.

 

처음에는 22번째 줄에 의해 몬스터가 초당 100 픽셀을

이동하는 속도로, 오른쪽으로 이동하겠죠.

 

그렇게 오른쪽으로 쭉 이동하다가 

m_fMaxDistance값만큼 이동하면

m_iDirection값에 -1을 곱해 방향을 전환해야겠죠?

 

 

이때 중요한 것이 24번째 줄에 있는 변수 fDist입니다.

 

몬스터가 움직일 기준점의 x 좌푯값에서

현재 몬스터가 위치한 x 좌푯값을 빼고

연산 결과를 abs 함수를 통해 절댓값으로 바꿉니다.

 

여기에서 몬스터가 움직일 최대 거리로

설정한 값을 빼고 fDist에 저장합니다.

 

26번째 줄에 있는 if 문을 통해 

fDist의 값이 0보다 크면, 즉 양수라면

28번째 코드에 의해 방향을 바꾸고

fDist의 부호를 바꿉니다.

 

현재 몬스터가 위치한 x 좌푯값과 fDist를 더해

다시 x 좌푯값에 저장합니다.

 

즉 fDist는 우리가 설정한 거리를 기준으로

얼마나 이동했는지에 대한 값을 저장하고

몬스터가 설정한 거리보다 더 움직였다면

더 움직인 만큼 몬스터의 위치를 빼주어 

몬스터의 위치를 조정해 줍니다.

 

몬스터가 설정한 거리보다 덜 움직였다면

fDist에 음수가 저장될 것이고

if 문에 걸리지 않아 마저 움직이게 될 겁니다.

 

 

예를 들어보면

 

몬스터가 움직일 기준점은

윈도우상 x 좌푯값을 640으로 하고,

설정한 최대 이동 거리는 50으로 했다고 합시다.

 

그럼 몬스터는 x 좌표 640을 기준으로

좌우로 움직일 것이며 처음에는 오른쪽부터

가게 되겠죠. 그리고 x 좌푯값이 50만큼 이동한

690가 되면 m_iDirection에 -1을 곱해

방향 전환이 발생할 겁니다.

 

 

근데 여기서 순간적으로 DeltaTime이 확 내려가 

 

실행 속도가 빨라져서 몬스터가 710 지점까지 

움직이고 말았습니다. 이대로 방향 전환을 하여

왼쪽으로 50만큼 이동하면 660 지점까지 이동하다가

다시 방향 전환이 되면서 몬스터가 이상하게 움직일 겁니다.

 

이를 방지하기 위한 24번째 줄에 있는

코드에 값을 대입해 보면

 

abs(640 - 710) - 50 = 20이

fDist에 대입되겠죠. 

 

양수 20이 fDist에 대입되었다는 것은

몬스터가 원래 도착해야 할 지점보다

20 픽셀 더 갔다는 것을 의미합니다.

 

이 경우 26번째 줄의 if 문에 걸려

방향 전환을 하여 그만 이동하게 하고

710 + 20 * -1 = 690의 값을

몬스터의 x 좌푯값에 대입함으로써

강제로 몬스터를 정상 위치에 옮겨 놓은 후에

다시 움직일 수 있도록 하는 겁니다.

 

 

만약 아래 그림처럼

 

순간적으로 Delta Time이 확 올라가

몬스터가 1초에 690 지점까지 가야 하는데

1초 동안 670 지점까지밖에 가지 못했습니다.

 

그러면

abs(640 - 670) - 50 = -20이

fDist에 대입되겠죠. 

 

-20이 fDist에 대입되었다는 것은

몬스터가 원래 도착해야 할 지점보다

20 픽셀 덜 갔다는 것을 의미합니다.

 

그래서 if 문에 걸리지 않고 

다음 프레임에 update 함수가 호출될 때

22번째 줄에 있는 코드가 실행되면서

남은 픽셀을 더 움직이게 되는 것이죠.

 

 

움직일 방향과 거리가 다 정해지고 난 후에야

33번째 줄에 있는 SetPos 함수가

호출되어 몬스터가 움직이기 위한

값들이 CObject 부모 클래스의 멤버에

이때 실제로 적용이 됩니다.

 

 

 

 


 

 

 

몬스터 오브젝트를 Start 씬에 출력하도록 하죠.

 

CScene_Start 클래스의 소스 파일에서

 

Start 씬이 실행되었을 때 초기 상태를

설정해 주는 Enter 함수를 보겠습니다.

 

22번째 줄에 있는 변수 CreateMonsterCount는 

Start 씬에 존재할 몬스터의 개수를 나타냅니다.

 

CreateMonsterMoveDistance는 존재하는 몬스터가

움직일 거리를 나타냅니다.

 

CreateMonsterScale는 몬스터의 크기를 나타내죠.

 

 

CreateMonsterCount가 5이므로 Start 씬에는

5개의 몬스터 오브젝트가 존재하는데 y 축 기준 동일 선상에,

x 축 기준으로는 일정한 간격을 두고 출력할 것입니다. 

 

이 간격을 계산하기 위해 27번째 줄에서 vResolution에 

CCore 클래스로 만든 코어 매니저가 갖고 있는 해상도를

대입하는 것이죠. 즉 프로그램이 실행되면 띄워지는

윈도우의 해상도를 갖고 오는 겁니다.

 

이때 GetResolution 함수의 반환형은 POINT이므로

Vec2형인 vResolution에 대입하려고 하면 오류가 발생하죠.

 

그래서 아래 코드처럼

 

구조체 Vec2의 구현 코드에서

8번째 줄에 대입 연산자 오버로드를 위해 

operator를 추가했습니다.

 

반환형은 Vec2 레퍼런스로

복사가 발생하지 않고 바로

참조할 수 있도록 하였죠.

 

인자로 받은 것을 const POINT형 레퍼런스로

하여 레퍼런스의 멤버 x, y를 Vec2의 멤버 x, y에 

대입하고 있습니다. POINT의 멤버 x, y는

long형이므로 Vec2의 float형 멤버인 x, y에

대입하기 위해 float으로 형변환해 주는 모습입니다.

 

하지만 대입 연산자를 오버로딩하여도

오류가 사라지지 않습니다.

 

이유는 27번째 줄에서 Vec2형 변수 vResolution을

선언함과 동시에 대입을 하고 있기 때문이죠.

 

선언과 동시에 대입을 하면 이를 대입으로 보지 않고

초기화로 인식합니다. 즉 Vec2형 vResolution가

생성됨과 동시에 우항에 있는 값으로 Vec2 생성자

을 통해 이니셜라이저를 진행하는 것입니다.

 

이를 위해 29번째 줄에 존재하는 생성자를 보시면

Vec2형 객체가 생성될 때 인자로 POINT형을 받으면 

각 멤버를 인자의 각 멤버로 초기화하도록 하는 것을

보실 수 있습니다.

 

 

변수 fTerm에 몬스터 간의 간격값이 계산되어 대입됩니다.

 

위 그림은 윈도우의 가로 해상도가 1280이고

50 크기의 몬스터가 50만큼 좌우로 이동하는 

프로그램인 경우입니다.

 

몬스터가 처음에는 회색 사각형에 존재하였다가

왼쪽으로 50만큼 이동하여 검은색 사각형 위치에 도달하겠죠.

 

이때 가장 왼쪽에 존재하는 몬스터가 움직이면서 화면 밖으로

나가지 않게, 딱 붙을 때까지만 이동하는 위치에 

생성하고 싶다면

 

몬스터가 이동하는 거리와 몬스터의 크기의 절반을

합한 값을 가장 왼쪽 몬스터의 x 좌푯값으로 하면 됩니다.

 

50 + 50 / 2 이므로 75가

가장 왼쪽 몬스터의 x 좌푯값이 되겠네요.

 

이 가장 왼쪽에 위치한 몬스터는 왼쪽으로

50만큼 이동하여도 잘리지 않고 화면 끝에

딱 걸치도록 움직일 것입니다.

 

 

가장 왼쪽에 존재하는 몬스터의 좌푯값을

구했으니 나머지 몬스터들의 좌푯값을 구해줄 수 있죠. 

( 이때 모든 몬스터는 일정한 크기로

일정한 거리를, 같은 방향으로 움직이며

일정한 간격을 두고 생성된다고 가정 )

 

여러 몬스터의 

간격을 구하고자 한다면 

일단 윈도우의 가로 해상도에서 몬스터가 이동하는 거리와

몬스터의 크기의 절반을 합한 값에 2를 곱한 값을 

빼주면 됩니다. 

 

1280 - ( 50 + 25 * 2 ) * 2 이므로 1130 입니다.

 

몬스터가 이동하는 거리와

몬스터의 크기의 절반을 합한 값에 

2를 곱하는 이유는 가로 해상도에서

왼쪽과 오른쪽 모두 75만큼 빼주기

위해서입니다.

 

그리고 이 값을 몬스터 오브젝트의 개수에서 

1을 뺀 값으로 나누어 줍니다.

 

몬스터 오브젝트의 개수에서 

1을 뺀 값으로 나누어 주는 이유는

만약 오브젝트가 5개라면 사이 간격의

개수는 4개죠? 오브젝트가 10개면

사이 간격의 개수는 9개일 겁니다.

사이 간격의 개수는 오브젝트 개수에서

항상 1이 적은 개수이므로 1을 빼 주어

간격의 값을 구하는 겁니다.

 

1130에서 몬스터의 개수 1을

빼 준 값으로 나누어 주면 되므로

 

1130 / 4 = 282 입니다.

 

즉 각 몬스터의 간격은 282라는 것이죠.

 

 

32번째 줄에 있는 for 문을 통해

 

본격적으로 몬스터를 생성해 줄 겁니다.

 

30번째 줄을 보시면 CMonster형 포인터를 선언하였고

34번째 줄에서 동적 할당된 공간을 가리켰습니다.

 

CMonster형 포인터로 가리키는 이유는

만약 부모 클래스인 CObject형 포인터로

자식 클래스인 CMonster형 공간을 가리키게 되면

CMonster 공간에 존재하는 몬스터의 속도, 최대 이동 거리 등

멤버에 접근할 수 없게 됩니다.

 

부모 클래스는 자식 클래스에 접근할 수 없기 때문이죠.

 

 

35번째 줄에서 몬스터가 움직일 기준점을

SetCenterPos 함수를 통해 설정해 주고 있습니다. 

 

i가 0일 때, 즉 첫 번째 몬스터의

움직일 기준점의 x 좌푯값은 

 

( 50 + 50 / 2 ) + ( 0 * 282 ) 일 겁니다.

 

y 좌푯값은 고정 50입니다.

 

i가 증가할수록 첫 번째 몬스터에서 282만큼

멀어지는 위치에 몬스터가 생성되겠죠.

 

 

36번째 줄에서는 SetPos 함수를 통해 

몬스터의 좌푯값을 설정해 주고 있습니다.

여기서 좌푯값은 몬스터의 중앙 좌푯값으로,

몬스터가 움직일 기준점과 같은 곳을

몬스터의 초기 좌푯값으로 설정해 주기 위해

GetCenterPos 함수의 반환값을 몬스터의 좌푯값으로

설정하고 있습니다.

 

 

37번째 줄에서 SetScale 함수를 통해

몬스터의 크기를 설정하고 있습니다.

 

SetScale 함수에 인자로

CreateMonsterScale값을 주어 

몬스터의 크기는 가로, 세로 50으로

설정되었습니다.

 

 

38번째 줄에서 SetMaxMoveDistance 함수를 통해

몬스터가 이동할 최대 거리를 설정하고 있습니다.

 

CreateMonsterMoveDistance값을 인자로,

즉 몬스터는 최대 50 픽셀까지 이동하게 되죠.

 

 

39번째 줄에서 AddObject 함수를 통해서 

CMonster형 포인터 pObjMonster가 가리키던

동적 할당된 공간에 존재하는 몬스터와

해당 몬스터가 속할 그룹값을 인자로 넘겨

씬이 보유하고 오브젝트가 모인 vector에

인자로 넘긴 몬스터를 vector에 삽입합니다.

 

 

이후 프로그램을 실행해 보면

 

5개의 몬스터(위쪽 사각형)가 일정한 간격으로 생성되어

좌우로 움직이는 것을 확인할 수 있으며

키 입력을 통해 플레이어(큰 사각형)를 움직일 수 있음을

확인할 수 있습니다.

 

또한 CPlayer 클래스와 CMonster 클래스는

똑같이 CObject 클래스를 상속받은 자식 클래스이지만

순수 가상 함수인 update 함수를 각 자식 클래스에서

다르게 구현하였기 때문에 키 입력이 발생하면

플레이어 오브젝트만 움직이고

몬스터 오브젝트에는 영향을 주지 않습니다.

 

 


강의 출처 : https://www.youtube.com/watch?v=fyNb6tK0vGU&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=17


'Win32 API > 기초' 카테고리의 다른 글

Win32 API 기초 : 기초 수학 (1)  (0) 2024.07.09
Win32 API 기초 : Object (2)  (0) 2024.07.08
Win32 API 기초 : Scene Manager (2)  (0) 2024.07.05
Win32 API 기초 : Scene Manager (1)  (0) 2024.07.04
Win32 API 기초 : Key Manager (2)  (2) 2024.07.03