** 게임루프와 일반메시지루프 **
게임은 일반 윈도우 어플리케이션과는 달리 별다른 메세지가 없어도 자기할일을 묵묵히(?)수행해야 하는 어플리케이션입니다. 덕분에 일반 어플리케이션과는 메세지 루프가 약간 다르죠.
1. 일반적인 메세지 루프
// 메세지가 올때까지 기다린다.
// 메세지가 생기면 메세지큐에서 메세지를 가져온다.
while(GetMessage(&msg....))
{
TranslateMessage(&msg,0,...);
DIspatchMessage(&msg,0...);
}
GetMessage는 메세지가 들어올때까지 블로킹상태로 진입하는 함수입니다.
메세지가 없을때 CPU를 다른 어플리케이션이 사용할수 있도록 해주는 목적이죠.
2. 일반적인 게임용 메세지 루프
while( WM_QUIT != msg.message )
{
// 메세지큐에 메세지가 있는지 검사한다.
// 메세지가 있건,없건 그냥 리턴한다.
if(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
{
//메세지 큐에서 메세지를 가져온다.
GetMessage(&msg,NULL,0,0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//메세지가 없다면 게임루프를 돌린다.
GameLoop();
}
}
PeekMessage는 메세지큐에 메세지가 들어와 있는지만을 검사하고 바로 리턴하는 함수 입니다. 맨 마지막에 붙어있는 PM_NOREMOVE 옵션은 메세지가 있는지 검사만 하고 빠져 나오도록 하기위함 입니다.
PeekMessage는 블로킹상태로 들어가는 함수가 아니므로 이런식으로 프로그램을 하면 while루프를 돌리기 위해 CPU를 계속해서 사용하게 됩니다. 하지만 게임은 어차피 계속해서 어떤일을 해야 하는 어플리케이션이니 별로 상관은 없겠죠.
다만 위 예제는 게임이 비활성화 되었을때의 처리가 없습니다.
3. DX8 예제나오는 메세지 루프
while( WM_QUIT != msg.message )
{
if( m_bActive ) bGotMsg = PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE );
else bGotMsg = GetMessage( &msg, NULL, 0U, 0U );
if( bGotMsg )
{
// Translate and dispatch the message
if( 0 == TranslateAccelerator( m_hWnd, hAccel, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
else
{
// Render a frame during idle time (no messages are waiting)
if( m_bActive && m_bReady )
{
if( FAILED( Render3DEnvironment() ) )
SendMessage( m_hWnd, WM_CLOSE, 0, 0 );
}
}
}
PeekMessage에 PM_REMOVE옵션을 쓴것에 주의하세요.
이렇게 하면 메세지가 있을경우엔 GetMessage와 동일한 동작을 합니다. 메세지가 없다면 PM_NOREMOVE옵션을 쓴거랑 똑같이 동작하죠.
m_bActive는 윈도우가 비활성화되었을때 게임이 돌지 않도록 하는겁니다.
윈도우가 비활성화 되었는데도 PeekMessage를 계속사용하는건 별로 바람직하지 못하기 때문에...( GetMessage를 쓰면 블로킹상태로 들어가는데 PeekMessage를 쓰면 계속해서 while 루프가 돌게되죠 ) ...
4. 함수형 메세지 루프
마지막으로 편법이지만 도스처럼 프램할수도 있습니다.
다음과 같은 일을하는 함수를 만들어서 될수 있는한 자주 호출해 주는거죠..
bool _ProcessMessage()
{
MSG msg;
bool bGotMsg;
while( 1 )
{
if( m_bActive ) bGotMsg = PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE );
else bGotMsg = GetMessage( &msg, NULL, 0U, 0U );
if( !bGotMsg ) return false;
TranslateMessage( &msg );
DispatchMessage( &msg );
if( WM_QUIT == msg.message ) return true; //exit...
}
return false;
}
이렇게 해놓구 게임에서 암때나 마구잡이로 호출하면 되죠. ...
WinMain(...)
{
...
while(1)
{
if( _ProcessMessage() )break;
//게임내용
...
}
//종료
...
}
이 방법의 장점은 도스처럼 마구잡이(?)프램이 가능하다는 것입니다.
프로그램 어디서건 _ProcessMessage()만 호출해주면 메세지처리는 되니깐 윈도우 메세지때문에 무한루프를 못돌리는 일은 없어집니다.
제가 생각해서 썼던거지만, 다른분들두 이렇게 하신분들이 분명 있을테고, 어떤 Mpeg2Player 소스를 보니 거기서도 이 방법을 썼더군요.
하지만.....
이 마지막 방법은 별루 권하구 싶지는 않은 방법이네요.
도스프램만 하시던 분이라면 몰라도, 첨 배우시는 분들은 제대로된 메세지 루프를 가지는 프램으로 만드시길...