DirectX11

DirectX11 - Tutorial 5 (실시간 메시지 루프)

재밌는게임~ 2024. 8. 3. 18:39

PeekMessage()라는 단일 함수에 대해 알아보고, 이 함수가 사악한 쌍둥이 함수인 GetMessage()와 어떻게 다른지 알아보겠습니다.

 

사실, GetMessage()에는 아무런 문제가 없습니다. 작동 방식이 게임과 게임의 지속적인 활동에 굉장한 결과를 가져오지 못할 뿐입니다. 이것이 어떤 것인지, 그리고 PeekMessage()가 어떻게 해결책인지 살펴보겠습니다.

 

GetMessage() 루프의 구조

이전 수업에서 우리는 GetMessage() 함수를 사용하여 간단한 Windows 애플리케이션을 빌드했습니다. 우리는 GetMessage()와 다른 두 함수를 사용하여 전송된 모든 Windows 메시지를 처리하는 루프를 만들었습니다. 그러나 당시에 이야기하지 않은 캐치가 있었습니다.

다음 다이어그램은 우리가 작성한 이벤트 루프가 작동하는 방식을 보여줍니다.

창을 만든 후 이벤트 루프로 들어가 GetMessage() 함수를 봅니다. 그런 다음 GetMessage()는 메시지를 기다렸다가 메시지를 받으면 다음 단계인 TranslateMessage()로 보냅니다. 이는 Windows 프로그래밍에 완벽하게 논리적입니다. 일반적으로 Windows 애플리케이션(예: Word)은 움직이기 전까지는 아무것도 하지 않고 가만히 있기 때문입니다.

하지만 이것은 우리에게는 잘 작동하지 않습니다. 이 모든 대기가 진행되는 동안, 우리는 초당 30~60개의 완전히 렌더링된 3D 이미지를 생성하여 지연 없이 화면에 표시해야 합니다. 그래서 우리는 다소 흥미로운 문제에 직면하게 되는데, Windows가 메시지를 보낸다면 초당 30개를 보내지는 않을 것이기 때문입니다.

 

새로운 기능, PeekMessage()

 

이 딜레마를 해결하기 위해 우리가 할 일은 현재의 GetMessage() 함수를 새로운 함수인 PeekMessage()로 대체하는 것입니다. 이 함수는 본질적으로 동일한 일을 하지만 중요한 차이점이 하나 있습니다. 아무것도 기다리지 않습니다. PeekMessage()는 메시지 큐를 살펴보고 대기 중인 메시지가 있는지 확인합니다. 그렇지 않으면 프로그램이 계속 진행되어 필요한 작업을 수행할 수 있습니다.

 

BOOL PeekMessage(LPMSG lpMsg,
                 HWND hWnd,
                 UINT wMsgFilterMin,
                 UINT wMsgFilterMax,
                 UINT wRemoveMsg);

 

처음 네 개의 매개변수는 여러분에게 익숙할 것입니다. 이들은 GetMessage()의 네 개의 매개변수와 동일합니다. 그러나 다섯 번째 매개변수인 wRemoveMsg는 새로운 것입니다.

이 함수가 하는 일은 이벤트 큐에서 검색된 메시지를 이벤트 큐에 유지해야 하는지 아니면 제거해야 하는지를 나타내는 것입니다. PM_REMOVE 또는 PM_NOREMOVE를 둘 수 있습니다. 첫 번째는 메시지를 읽을 때 큐에서 메시지를 제거하는 반면, 두 번째는 나중에 검색할 수 있도록 메시지를 그대로 둡니다. 여기서는 PM_REMOVE 값을 사용하고 간단하게 유지하겠습니다.

 

그럼 이걸 우리 프로그램에 어떻게 구현할까요? 다음은 우리가 만든 마지막 프로그램의 메인 루프로, PeekMessage()를 사용하도록 수정했습니다.

 

// 메인 루프로 들어갑니다.

// 이 구조체는 Windows 이벤트 메시지를 보관합니다.
MSG msg = {0};

// 무한 메시지 루프로 들어갑니다.
while(TRUE)
{
    // 큐에 대기 중인 메시지가 있는지 확인합니다.
    if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
        // 키 입력 메시지를 올바른 형식으로 변환합니다
        . TranslateMessage(&msg);

        // 메시지를 WindowProc 함수로 보냅니다.
        DispatchMessage(&msg);

        // 종료할 시간인지 확인합니다.
        if(msg.message == WM_QUIT)
            break;
    }
    else
    {
        // 여기에서 게임 코드를 실행합니다.
        // ...
        // ...
    }
}

 

이제 우리 프로그램은 Windows와 그 지루한 메시지에 대해 걱정할 필요 없이 원하는 대로 시기적절하게 일을 처리할 수 있습니다. 우리가 한 변경 사항을 빠르게 살펴보겠습니다.

 

while(TRUE)

당연히 이것은 무한 루프를 만듭니다. 우리는 나중에 게임을 종료할 때 탈출할 것입니다.

if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

더 이상 메시지를 기다리지 않고, 그저 무엇이 있는지 살펴보기 위해 엿보기만 하기 때문에 PeekMessage()를 사용할 것입니다. PeekMessage()는 메시지가 있으면 TRUE를 반환하고 없으면 FALSE를 반환합니다. 따라서 메시지가 있으면 TranslateMessage()와 DispatchMessage()를 실행하고, 메시지가 없으면 게임 코드로 넘어갑니다

if(msg.message == WM_QUIT)

메시지가 WM_QUIT인 경우, "무한" 루프를 종료하고 Windows로 돌아갈 때입니다. 이전에는 GetMessage()가 종료할 때 항상 '0'을 반환하여 while() 루프를 끊었기 때문에 이런 기능이 없었습니다. 안타깝게도 여기에는 그런 메커니즘이 없고, 직접 찾아야 합니다.

 

 

728x90