DirectDrawの使い方 第0回
sept/村木亮太
DirectDrawを使うまでの下準備について説明します。
int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // pointer to command line int nCmdShow // show state of window ) { if( !theApp.Create(hInstance,nCmdShow) ) return 0; return theApp.Run(); }
やっていることは、Windowsからもらったいろいろな値を、そのままCreateに渡し、ウィンドウ作成に失敗したらそこで実行を止めます。ウィンドウ作成に成功していたら、メッセージを処理するためのループを呼び、そのループが終わるのを待ちます。
BOOL RxGameApp::Create(HINSTANCE hInst,int nCmdShow) { m_hInst = hInst; WNDCLASSEX wndclass; wndclass.cbSize = sizeof(WNDCLASSEX); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInst; wndclass.hIcon = LoadIcon(hInst,GetAppIcon()); wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ); wndclass.hbrBackground = (HBRUSH)GetStockObject( GRAY_BRUSH ); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = GetClassName(); wndclass.hIconSm = NULL; RegisterClassEx(&wndclass); m_hWndMain = CreateWindow(GetAppName() ,GetAppTitle() ,WS_POPUP | WS_VISIBLE | WS_SYSMENU | WS_CAPTION ,0 ,0 ,GetSystemMetrics(SM_CXSCREEN) ,GetSystemMetrics(SM_CYSCREEN) ,HWND_DESKTOP ,NULL ,hInst ,NULL); SetCursor(NULL); if (!m_hWndMain) return FALSE; SetWindowLong( m_hWndMain, GWL_USERDATA, (LONG)this ); if ( !OnCreate() ) return FALSE; ShowWindow(m_hWndMain,SW_SHOW); UpdateWindow(m_hWndMain); SetFocus( m_hWndMain ); m_bActive = TRUE; return TRUE; }
wndclassにいろいろな値を設定しているのは、どんなウィンドウを作るかということを設定しています。大事なのはwndclass.lpfnWndProc = WndProc;の部分です。これは、Windowsがメッセージを送るときにWndProcを呼ぶように設定しています。
m_hWndMain = CreateWindow(GetAppName() ,GetAppTitle() ,WS_POPUP | WS_VISIBLE | WS_SYSMENU | WS_CAPTION ,0 ,0 ,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN) ,HWND_DESKTOP ,NULL ,hInst ,NULL);
この部分はCreateWindowというAPIを呼んで実際にウィンドウを作ります。この三行目にある二つの引数がウィンドウの左上の座標になります。そして次の行の引数がサイズになります。ここでは、画面全体を覆うのでGetSystemMetricsというAPIを呼んでサイズを調べています。もし作成に失敗するとCreateWindowは0を返すので0が返ってきたときには、そこでやめます。そのあとOnCreateを呼んでそこで初期化ができるようにして起きます。その後細かいことをいろいろとやってTRUEを返しておしまいです。
long CALLBACK RxGameApp::WndProc(HWND hwnd,UINT message,UINT wParam,LONG lParam) { RxGameApp* app = (RxGameApp*)GetWindowLong(hwnd, GWL_USERDATA); if(app){ return app->Dispatch(hwnd, message, wParam, lParam); }else{ return DefWindowProc(hwnd, message, wParam, lParam); } }
ここでは、Create関数の
SetWindowLong( m_hWndMain, GWL_USERDATA, (LONG)this );
の部分で設定したポインタを取り出してもし取り出せたらDispatchを呼び、そうでなかったらデフォルトのメッセージ処理APIを呼んでいます。
long RxGameApp::Dispatch( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { switch (message) { case WM_ACTIVATEAPP: if ( LOWORD(wParam) == WA_INACTIVE ) { m_bActive = FALSE; OnDeactivate(); }else{ OnActivate(); m_bActive = TRUE; } return 0; case WM_SETCURSOR: SetCursor(NULL); return TRUE; case WM_KEYDOWN: if(wParam == VK_F12){ Close(); }else{ OnKeyDown(wParam); } return 0; case WM_DESTROY: OnDestroy(); PostQuitMessage(0); return 0; } return DefWindowProc (hWnd , message , wParam,lParam); }
ここで処理しているのはWM{\_}ACTIVATEAPP、WM{\_}SETCURSOR、WM{\_}KEYDOWNと\\ WM{\_}DESTROYだけです。それぞれウィンドウがアクティブになったとき、カーソルがウィンドウ内に入ったときキーが押されたとき、ウィンドウが閉じられたときに対応します。それぞれに対応するOn〜を呼んでいます。
int RxGameApp::Run() { MSG msg; for (;;) { if ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) { if ( !GetMessage( &msg, NULL, 0, 0 ) ) { break; } TranslateMessage(&msg); DispatchMessage(&msg); } else { if ( m_bActive ) { Loop(); } } } return msg.wParam; }
ここでは、PeekMessageでメッセージが来ているかを調べ、もし来ていればそれを処理し、もし無ければLoopを呼んでいます。Loopに実際のゲーム処理を書きます。
にあるRxGameApp.h、RxGameApp.cppやStdAfx.hなどを見てください。
HTML化 村木亮太、東京大学教養学部前期課程, TSG(理論科学グループ)