나는 지난 2주간 어소트락 winapi 강좌를 보면서 프레임워크 만드는 것을 진행했다. 강좌의 진도가 빠르고 코드의 양이 워낙 많은 탓에 프레임워크 만드는 것을 따라했다고 봐도 무방할 것 같다. 그래서 강좌를 모두 시청한 지금 시점에서 다시 코드를 복습하고 배운 것들을 정리하려고 한다.
#include <Windows.h>
#include "Core.h"
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
if (!CCore::GetInst()->Init(hInstance))
{
CCore::DestroyInst();
return 0;
}
int iRev = CCore::GetInst()->Run();
CCore::DestroyInst();
return 0;
}
먼저 main함수이다. wWinMain은 winapi에서 main함수 역할을 한다고 이전에 글에 작성한 적이 있다. Core 클래스는 프레임워크에서 가장 기반이 되는 클래스로 윈도우 창을 띄우고 메세지 루프를 돌리며 실질적인 프레임워크의 로직이 되는 함수들을 실행한다. wWinMain에서는 Core클래스를 싱글톤 패턴으로 접근한다. 싱글톤 패턴은 한개의 인스턴스만 생성해도 되는 관리자 클래스에 적합하기 때문에 이후에 관리자 클래스들에도 싱글톤 패턴을 이용한다.
bool CCore::Init(HINSTANCE hInst)
{
m_hInst = hInst;
MyRegisterClass();
//해상도 설정
m_tRS.iW = 1280;
m_tRS.iH = 720;
Create();
//화면 DC 생성
m_hDC = GetDC(m_hWnd);
//타이머 초기화
if (!GET_SINGLE(CTimer)->Init(m_hWnd))
return false;
//경로 관리자 초기화
if (!GET_SINGLE(CPathManager)->Init())
return false;
//입력 관리자 초기화
if (!GET_SINGLE(CInput)->Init(m_hWnd))
return false;
//리소스 관리자 초기화
if (!GET_SINGLE(CResourcesManager)->Init(hInst,m_hDC))
return false;
//카메라 관리자 초기화
if (!GET_SINGLE(CCamera)->Init(POSITION(0.f, 0.f),
m_tRS,RESOLUTION(1500,1200)))
return false;
//장면관리자 초기화
if (!GET_SINGLE(CSceneManager)->Init())
return false;
//사운드관리자 초기화
if (!GET_SINGLE(CSoundManager)->Init())
return false;
return true;
}
Core의 Init에서는 클래스의 정보를 설정하고 등록하고, (RegisterClass) 창을 생성하고 띄운다. (CreateWindow) 그리고
여러 초기화가 필요한 관리자 클래스들의 초기 정보를 설정한다. Init은 단 한번만 실행된다.
int CCore::Run()
{
MSG msg;
// 기본 메시지 루프입니다:
while (m_bLoop) {
//PeekMessage는 메세지가 메세지 큐에 없어도 바로 빠져나온다.
//메세지가 있을 경우 true 없을 경우 false가 된다.
//메세지가 없는 시간이 윈도우의 데드 타임이다.
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//윈도우 데드타임일 경우
else
{
Logic();
}
}
return (int)msg.wParam;
}
Run 함수는 메세지 루프를 만들어 게임 Logic 함수 실행을 반복한다. PeekMessage는 메세지큐에 메세지가 있으면 true, 없으면 false를 반환하는데 게임같이 별도 메시지가 없어도 계속해서 처리해야 하는 것이 있으면 PeekMessage가 false일 때 실행되는 else 블럭에서 게임의 Logic을 실행한다.
void CCore::Logic()
{
//타이머 갱신
GET_SINGLE(CTimer)->Update();
float fDeltaTime = GET_SINGLE(CTimer)->GetDeltaTime();
Input(fDeltaTime);
if (Update(fDeltaTime) == SC_CHANGE)
return;
if (LateUpdate(fDeltaTime) == SC_CHANGE)
return;
Collision(fDeltaTime);
Render(fDeltaTime);
}
메세지 루프는 너무 빠른 속도로 반복하기 때문에 프로그램의 시간단위를 정하기 위해서 DeltaTime을 Timer클래스에서 설정한다. 그리고 이후로 DeltaTime을 기준으로 함수를 실행한다. 프레임워크의 로직은 Input, Update, LateUpdate, Collision, Render, 크게 5개로 나뉜다.
Input: 키보드, 마우스 등 모든 입력을 감지한다. 그리고 Scene에서 감지한 입력에 할당된 함수들을 실행한다.
방향키를 이용해 플레이어, 카메라가 움직이고 클릭을 통해 맵타일을 수정하는 것 등이 모두 Input에서 진행된다.
Update: 캐릭터의 이동,공격 모션 등 애니메이션에 관한 처리를 한다. 모션이 부드럽게 보이도록 프레임 별로 이미지를 업데이트한다. 또한 카메라가 맵 밖으로 나가지 못하도록 제한한다.
LateUpdate: 충돌 처리를 하는 Collider의 위치를 업데이트한다. Input에서 입력을 받아 캐릭터의 이미지 위치를 옮기면, LateUpdate에서는 캐릭터의 충돌체 위치를 옮긴다.
Collision: 실체 충돌을 처리한다. LateUpdate에서 업데이트한 충돌체의 위치를 바탕으로 두개 이상의 충돌체들이 서로 충돌했는지 하지 않았는지 계산을 통해 처리한다.
Render: 화면에 게임이 보여지도록 한다. BitBlt함수를 이용하고 화면에 잔상이 남거나 깜빡이지 않게 하기 위해 더블 버퍼링을 이용한다. 더블 버퍼링은 백버퍼를 하나 만들어 거기에 렌더링한 것들을 출력하고 다시 백버퍼에서 프런트 버퍼로 출력한다.
'WinAPI' 카테고리의 다른 글
| [WinAPI] 게임 프레임워크 - 3. Reference Count (0) | 2022.02.01 |
|---|---|
| [WinAPI] 게임 프레임 워크 - 2. Scene과 SceneManager (0) | 2022.02.01 |
| [WinAPI] Pivot (0) | 2022.01.20 |
| [WinAPI] 더블 버퍼링 (0) | 2022.01.20 |
| [WinAPI] 삼각함수 (0) | 2022.01.16 |