1. Main 함수

더보기
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    // TODO: 여기에 코드를 입력합니다.

    // 1) 윈도우 창 정보 등록
    MyRegisterClass(hInstance);

    // 2) 초기화
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }


    MSG msg;

    //3) 메인 루프
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int) msg.wParam;
}

 

1) 더미 코드 ( 불필요 )

    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
    
    //~~
    //단축키가 할당되어있음
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GAMECODING));
    
    //~~
    // hAccelTable의 조건도 같이 삭제
    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))

실행될 때 제거되는 더미 코드와 쓸모 없는 코드들을 삭제한다.

 

 

2) szWindowClass ( 중요도 낮음 )

    // 전역 문자열을 초기화합니다.
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_GAMECODING, szWindowClass, MAX_LOADSTRING);

리소스 파일 내 포함된 문자열(프로그램 제목)을 초기화하는 구문이다.

szWindowClass는 MyRegisterClass 안에 포함되어 있다. 따로 설정하였다면 삭제한다.

 

3) 메인 루프

    //3) 메인 루프
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

게임의 본격적인 코드가 들어가는 루프이다.

 

 

2. 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);
}

-> 아이콘, 커서, 창을 초기화한다.  RegisterClassExW 함수를 반환한다.

wcex.lpfnWndProc    = WndProc;

-> 윈도우의 메시지를 처리하고 작업한다.

 

1) 메뉴를 제거하고 창의 이름을 바꾸기

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    // ~~
    wcex.lpszMenuName   = nullptr;
    wcex.lpszClassName  = L"GameCoding";
    // ~~
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.

   HWND hWnd = CreateWindowW(L"GameCoding", L"Client", WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
      //~~

 

 

3. 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;
}

최초 초기화에 구동되는 코드이다.

hWnd와 AdjustWindowRect 함수의 값을 받는 windowRect를 사용하여 윈도우의 크기를 800 * 600으로 변경할 수 있다.

 

 

4. WndProc 함수, BeginPaint

메시지가 들어오면 윈도우 프로시져를 이용하여 해결한다.

타입의 앞자리에 h는 int를 뜻한다(커널과 프로그램 사이의 통신은 숫자로 이루어짐).

 

 

1) 문자 표현하기

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다...
            
            // 문자 그리기
            ::TextOut(hdc, 100, 100, L"TEST", 4);

hdc는 일종의 도화지 역할을 한다.

TestOut 함수를 사용하여 hdc, 시작 좌표(x, y), 문자열, 문자열의 길이를 적어주면 된다.

 

2) 그림 표현하기

            // 사각형 그리기
            ::Rectangle(hdc, 200, 200, 400, 400);

            // 원 그리기
            ::Ellipse(hdc, 200, 200, 400, 400);

사각형과 원은 문자와 다르게 끝나는 좌표도 적어주어야 한다.

 

3) 선 표현하기

            // 선 그리기
            ::MoveToEx(hdc, 300, 300, nullptr);
            ::LineTo(hdc, 400, 400);

선의 시작점을 MoveToEx 함수로 지정해주고 끝점을 LineTo 함수로 지정해준다.

 

WM_PAINT는 화면이 움직이거나, 크기가 바뀔 때 다시 그려주도록 작동한다.

 

 

5. WndProc 함수, WM_MOUSTMOVE

    case WM_MOUSEMOVE:

        // lParam & 0xFFFF;
        // lParam -> 16

        int x = LOWORD(lParam);
        int y = HIWORD(lParam);
        
        break;

마우스의 좌표를 추적하는 함수이다.

마우스의 x 좌표는 16비트의 낮은 8비트로 나타나고, 마우스의 y 좌표는 16비트의 큰 쪽 8비트로 나타난다.

 

1) 마우스의 좌표를 출력하기

위의 x와 y를 전역변수로 선언한다.

int mousePosX;
int mousePosY;
    case WM_MOUSEMOVE:

        // lParam & 0xFFFF;
        // lParam -> 16

        mousePosX = LOWORD(lParam);
        mousePosY = HIWORD(lParam);
        ::InvalidateRect(hWnd, nullptr, TRUE);
        break;

InvalidateRect를 이용하여 WM_PAINT를 강제로 호출하여 매번 그려준다.

 

이후에 WM_PAINT를 다음과 같이 수정해준다.

case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다...
            
            // 문자 그리기
            WCHAR buffer[100];
            ::wsprintf(buffer, L"(%d, %d)", mousePosX, mousePosY);


            ::TextOut(hdc, 100, 100, buffer, ::wcslen(buffer));

마우스를 움직일 때마다 마우스의 좌표가 그려진다.

 

 

6. 결론

WM_PAINT는 매 프레임마다 초기화되지 않기 때문에 게임을 만들기에는 적합하지 않다.