windowsでゲームループを作る。

C/C++windowsでゲームを作る時、ウィンドウクラスを登録して、ウィンドウを作って、メッセージループを回して、ウィンドウプロシージャを用意して…と、非常に面倒ですよね。

正直main関数内でそのようなつまらない記述を見たくないわけです。

もっと素直に

  • ウィンドウを作って
  • ウィンドウが閉じられるまでゲームループを回し、
  • 1フレームの処理

を記述したいのです。

int main()
{
    ゲーム用のウィンドウを生成;

    //ゲームループ
    while( ウィンドウが閉じられるまで )
    {
        1フレームの処理;
    }

    return 0;
}

ということで、以下適当にサンプルを。

main関数はできるだけシンプルに。
#include <windows.h>

int main()
{
    //ゲーム用のウィンドウを生成
    HWND hwnd = CreateGameWindow( "hoge", 720, 480 );
    if( hwnd == nullptr )return 0;

    //ゲームループ
    while( !IsClosed( hwnd ) )  //ウィンドウが閉じられるまで
    {
        //1フレームの処理
        ;

        //windowsイベントがあればウィンドウプロシージャを実行する。
        DoEvents();
    }

    return 0;
}
各関数の実装。

面倒だったので、ウィンドウプロシージャも無しの方向で。

/**
 *  指定サイズのクライアント領域を持つ、ゲーム用の単純なウィンドウを作成する
 *  @param  title   ウィンドウのタイトルを指定
 *  @param  width   クライアント領域の幅を指定
 *  @param  height  クライアント領域の高さを指定
 *  @return         生成したウィンドウのハンドル。失敗時はNULLを返す。
 */
HWND CreateGameWindow( const std::string& title, int client_width, int client_height )
{
    const std::string class_name = "hoge";
    const DWORD style = WS_OVERLAPPEDWINDOW & ~( WS_MAXIMIZEBOX | WS_THICKFRAME );
    const DWORD exstyle = 0;
    const HINSTANCE instance = GetModuleHandle( nullptr );

    //ウィンドウクラスの登録
    WNDCLASSEX wc = { sizeof( WNDCLASSEX ) };
    wc.hInstance = instance;
    wc.lpszClassName = class_name.c_str();
    wc.lpfnWndProc = DefWindowProc;
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
    wc.hIcon = static_cast< HICON >(
        LoadImage( nullptr, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED )
        );
    wc.hIconSm = wc.hIcon;
    wc.hCursor = static_cast< HCURSOR >(
        LoadImage( nullptr, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED )
        );
    wc.hbrBackground = static_cast< HBRUSH >(
        GetStockObject( WHITE_BRUSH )
        );
    if( !RegisterClassEx( &wc ) )return nullptr;

    //クライアントサイズからウィンドウサイズを計算
    RECT rect = { 0, 0, client_width, client_height };
    AdjustWindowRectEx( &rect, style, FALSE, exstyle );

    //ウィンドウ生成
    HWND hwnd = CreateWindowEx(
        exstyle, class_name.c_str(), title.c_str(), style,
        CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top,
        nullptr, nullptr, instance, nullptr 
        );
    if( hwnd == nullptr )return nullptr;
    ShowWindow( hwnd, SW_SHOW );

    return hwnd;
}

/**
 *  windowsイベントがあればプロシージャを実行する。
 *
 *  @return WM_QUITメッセージを受け取ったかどうか
 */
bool DoEvents()
{
    MSG msg;
    for(;;)
    {
        if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
        {
            //ウィンドウが閉じられた
            if( msg.message == WM_QUIT )return false;

            TranslateMessage( &msg );
            DispatchMessage( &msg );        //ウィンドウプロシージャの呼び出し
        }
        else
        {
            //ゲームループで1フレームの処理を行う
            return true;
        }
    }
}

/**
 *  ウィンドウが閉じられたか
 *
 *  @param  hwnd    ウィンドウハンドル
 */
bool IsClosed( HWND hwnd )
{
    return ::IsWindow( hwnd ) == FALSE;
}
  • ウィンドウが閉じられたかどうか調べる際に、IsWindowだけ問題ないのか
実行結果


サンプルのダウンロード