기본 템플릿 분석

Jaemyeong Lee·2024년 12월 6일
0

1. 기본 구조 및 새 프로젝트 생성

Windows API를 활용해 미니 엔진을 제작하는 과정은 게임 개발의 첫 단계로, Windows 데스크톱 애플리케이션 템플릿을 분석하며 시작합니다.

  1. 새 프로젝트 만들기
    • Windows 데스크톱 애플리케이션을 선택하여 새 프로젝트를 생성합니다.
    • 프로젝트 이름을 GameCoding으로 설정하여 시작합니다.

2. main 함수 분석

main 함수는 Windows 애플리케이션의 시작점으로, wWinMain 함수로 구현되어 있습니다.

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
  • hInstance: 현재 애플리케이션 인스턴스를 식별하는 핸들입니다.
  • hPrevInstance: 이전 인스턴스 핸들입니다. Windows 95 이후 의미가 없습니다.
  • lpCmdLine: 명령줄 인수로 전달된 문자열입니다.
  • nCmdShow: 창 표시 상태를 지정합니다.

전역 문자열 초기화 및 윈도우 정보 등록

  • 템플릿에서는 LoadStringW를 통해 문자열을 초기화하지만, 분석 단계에서는 불필요한 코드를 제거합니다.
MyRegisterClass(hInstance);
  • MyRegisterClass: 창 클래스를 등록하는 함수입니다.

윈도우 창 생성 및 애플리케이션 초기화

if (!InitInstance(hInstance, nCmdShow))
{
    return FALSE;
}
  • InitInstance: 주 창을 생성하고 표시하는 역할을 합니다.

메시지 루프

while (::GetMessage(&msg, nullptr, 0, 0))
{
    ::TranslateMessage(&msg);
    ::DispatchMessage(&msg);
}
  • GetMessage: 메시지 큐에서 메시지를 가져옵니다. nullptr은 모든 메시지를 대상으로 지정합니다.
  • TranslateMessage: 키보드 메시지를 추가적으로 변환합니다.
  • DispatchMessage: 메시지를 처리하기 위해 창 절차(WndProc)로 보냅니다.

3. MyRegisterClass 함수 분석

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GAMECODING));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName = nullptr;
    wcex.lpszClassName = L"GameCoding";
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}
  • WNDCLASSEXW** 구조체**: 윈도우 클래스 정보를 저장합니다.
    • lpfnWndProc: 윈도우 메시지를 처리하는 콜백 함수로 WndProc 지정.
    • hCursor: 마우스 커서를 설정.
    • lpszClassName: 창 클래스 이름.
  • RegisterClassExW: 위에서 설정한 클래스를 등록합니다.

4. InitInstance 함수 분석

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 전역 변수에 인스턴스 핸들 저장

   RECT windowRect = { 0, 0, 800, 600 };
   ::AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, false);

   HWND hWnd = CreateWindowW(L"GameCoding", L"Client", WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, windowRect.right - windowRect.left,
      windowRect.bottom - windowRect.top, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ::ShowWindow(hWnd, nCmdShow);
   ::UpdateWindow(hWnd);

   return TRUE;
}
  • AdjustWindowRect: 클라이언트 영역을 원하는 크기로 맞춥니다.
  • CreateWindowW: 윈도우를 생성합니다.
    • CW_USEDEFAULT: 기본 위치와 크기를 설정합니다.
    • nullptr: 부모 창과 메뉴를 설정하지 않음.

5. WndProc 함수 분석

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        // 명령 처리
        break;
    case WM_PAINT:
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        WCHAR buffer[100];
        ::wsprintf(buffer, L"(%d , %d)", mousePosX, mousePosY);
        ::TextOut(hdc, 100, 100, buffer, ::wcslen(buffer));
        ::Rectangle(hdc, 200, 200, 400, 400);
        ::Ellipse(hdc, 200, 200, 400, 400);
        ::MoveToEx(hdc, 300, 300, nullptr);
        ::LineTo(hdc, 400, 400);
        ::LineTo(hdc, 500, 300);
        EndPaint(hWnd, &ps);
        break;
    case WM_MOUSEMOVE:
        mousePosX = LOWORD(lParam);
        mousePosY = HIWORD(lParam);
        ::InvalidateRect(hWnd, nullptr, TRUE);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
  • WM_PAINT: 창을 다시 그릴 때 호출됩니다.
    • ::TextOut: 텍스트 출력.
    • ::Rectangle, ::Ellipse: 사각형과 원 그리기.
    • ::MoveToEx, ::LineTo: 선 그리기.
  • WM_MOUSEMOVE: 마우스 이동 이벤트 처리.
    • LOWORD(lParam), HIWORD(lParam): X, Y 좌표를 분리.
    • ::InvalidateRect: 강제로 WM_PAINT 호출.
  • WM_DESTROY: 애플리케이션 종료 메시지를 전달.

6. 문제점 및 개선 사항

  1. 기본 메시지 루프의 한계

    • GetMessage는 메시지가 없으면 대기 상태로 들어갑니다.
    • 실시간 처리(예: 몬스터 AI, 물리 엔진)를 위해 무한 루프가 필요합니다.
  2. WM_PAINT 사용의 한계

    • WM_PAINT는 창 크기 변경 또는 최소화/복원 시 호출되므로, 지속적인 업데이트에 적합하지 않습니다.
    • 게임에서는 별도의 렌더링 루프를 구현해야 합니다.

profile
李家네_공부방

0개의 댓글