windowsでゲームループを作る。(2)〜Windowクラス〜

VC++2010を使い始めたので、出来るだけC++0xの機能を使いつつ、前回(http://d.hatena.ne.jp/setuna-kanata/20100512/1273685558)のプログラムを書き換えていきます。

Windowクラス

まずはHWNDのラッパークラスであるWindowクラスを作ります。今回必要になるメソッドは

  • コンストラク
  • ウィンドウが閉じられたかどうか調べるメソッドIsClosed
    • とりあえずIsWindow関数を使用

の2つです。

Window.h
#include <windows.h>

/**
 *  HWNDのラッパークラス
 */
class Window
{
public:
    /**
     *  コンストラクタ
     */
    Window( HWND hwnd );

public:
    /**
     *  ウィンドウが閉じられたか
     */
    bool IsClosed()const{ return ::IsWindow( hwnd ) == FALSE; }

private:
    HWND hwnd;
};
Window.cpp
#include "Window.h"

/**
 *  コンストラクタ。生成済みのウィンドウハンドルを渡す。
 */
Window::Window( HWND hwnd )
    : hwnd( hwnd )
{
}
Applicationクラス

C#風にwindowsイベントを処理するためにDoEvents関数を作ります。

  • windowsイベントを処理するためのメソッドDoEvents
  • ゲーム用ウィンドウを生成するメソッドCreateGameWindow
    • ウィンドウはstd::shared_ptr< Window >で返す
    • 前回同様、面倒なのでウィンドウプロシージャは用意せず、DefWindowProcでデフォルトの処理のみ行う。
Application.h
#include <string>
#include <memory>

class Window;
typedef std::shared_ptr< Window > WindowPtr;

class Application
{
public:
    /**
     *  メッセージキューにある全てのwindowsイベントを処理する。
     */
    static bool DoEvents();

    /**
     *  指定サイズのクライアント領域を持つ、ゲーム用の単純なウィンドウを作成する
     */
    static WindowPtr CreateGameWindow( const std::string& title, int client_width, int client_height );

};
Application.cpp
#include "Application.h"
#include "Window.h"
#include <iostream>

/**
 *  メッセージキューにある全てのwindowsイベントを処理する。
 *
 *  @return WM_QUITメッセージを受け取ったかどうか
 */
bool Application::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  title   ウィンドウのタイトルを指定
 *  @param  width   クライアント領域の幅を指定
 *  @param  height  クライアント領域の高さを指定
 *  @return         生成したWindowクラスへのstd::shared_ptrを返す。失敗時はnullptrを返す。
 */
WindowPtr Application::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 );

    WindowPtr window( new Window( hwnd ) );

    std::cout << "ウィンドウを生成( " << title << "," << client_width << "," << client_height << " )" << std::endl;
    return window;
}
使い方
#include "Application.h"
#include "Window.h"

/**
 *  エントリーポイント
 */
int main()
{
    //メモリリークの検出を有効化
    //  アウトプットウィンドウにメモリリーク時の情報が出力される
    ::_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

    //ゲーム用ウィンドウを生成
    WindowPtr window = Application::CreateGameWindow( "hoge", 720, 480 );
    if( !window )return 0;
    
    //ゲームループ
    //  ウィンドウが閉じられるまで1フレームの処理を繰り返す
    while( !window->IsClosed() )
    {
        //1フレームの処理
        ;

        //メッセージキューにある全てのwindowsイベントを処理する。
        Application::DoEvents();
    }

    return 0;
}
サンプルのダウンロード