プログラミングのメモ帳(C/C++/HSP)

日々のプログラミングで気づいた点や小技集を紹介します。(Windows 10/XP/Vista、VC2017、HSP)

付録B ゲームのフレームワーク関数

2012年03月10日 05時31分02秒 | 無料で学べる講座

このページはゲームループの基礎講座シリーズの付録Bです。
ここには、この講座で紹介してる「ゲームのフレームワーク関数」である GameMainLib.cpp のソース・ファイルを紹介します。(戻る)

使い方

ゲーム・プログラマは、次の手順で WinMain 関数に記述します。

  1. GameMainInit 関数でスクリーンの横幅、高さ、フレーム数(FPS)を設定する。
  2. GameMainCreate 関数でゲーム・ウインドウを作成する。
  3. GameMainRun 関数にゲームの4つの基本処理を行う関数名を渡して実行する。
  4. エラーが発生した時のために GameMainErrMsg 関数を呼び出す記述を書く。
//------------------------------------------------
// メイン関数(OK)
//------------------------------------------------
extern int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE, LPTSTR, int nCmdShow )
{
    LPCTSTR lpClassName = TEXT("GameNameWndClass");
    LPCTSTR lpTitleName = TEXT("ゲームのタイトル");
    
    if ( GameMainInit(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_FPS) ){
        if ( GameMainCreate(hInstance,lpClassName,lpTitleName,nCmdShow) ){
            return GameMainRun( funcGameInit, funcGameTerm, funcGameUpdate, funcGameDraw );
        }
    }
    return GameMainErrMsg( NULL, TEXT("WinMain関数") );
}

GameMainLib.cpp

//------------------------------------------------------------------------------
// ゲームのフレームワーク関数
//------------------------------------------------------------------------------
#include <Windows.h>    // WinMM.Lib
#include "GameMainLib.h"

//------------------------------------------------
// break 付きのキーワード
//------------------------------------------------
#define CASE        break;case
#define DEFAULT     break;default

//------------------------------------------------
// ウインドウのスタイル
//------------------------------------------------
#define WINDOW_STYLE        (WS_OVERLAPPEDWINDOW ^ (WS_THICKFRAME|WS_MAXIMIZEBOX))

//------------------------------------------------
// グローバル変数
//------------------------------------------------
static HWND gWnd;           // ウインドウのハンドル
static UINT gFPS;           // フレームの更新間隔
static BOOL gPause;         // フレームの一時停止
static LONG gWidth;         // スクリーンの横幅
static LONG gHeight;        // スクリーンの縦幅
static LONG gErrCode;       // エラー・コード値

//------------------------------------------------
// ウインドウのプロシージャ関数
//------------------------------------------------
static LRESULT CALLBACK mainWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    HDC hScreenDC = (HDC)GetWindowLong(hWnd,GWL_USERDATA);
    
    switch ( uMsg ){
        CASE WM_CREATE:
            hScreenDC = GameMainMemDC( hWnd, gWidth, gHeight );
            SetWindowLong( hWnd, GWL_USERDATA, (LONG)hScreenDC );
        CASE WM_CLOSE:
            DeleteDC( hScreenDC );
            DestroyWindow( hWnd );
        CASE WM_DESTROY:
            PostQuitMessage( 0 );
        CASE WM_PAINT:
        {
            PAINTSTRUCT     ps;
            HDC             hDC;
            
            hDC = BeginPaint( hWnd, &ps );
            BitBlt( hDC, 0, 0, gWidth, gHeight, hScreenDC, 0, 0, SRCCOPY );
            EndPaint( hWnd, &ps );
        }
        CASE WM_ACTIVATE:
        {
            LONG nActive        = LOWORD(wParam);
            BOOL bMinimized     = HIWORD(wParam);
            
            if ( nActive == WA_INACTIVE ){      // 非アクティブ時
                gPause = TRUE;
            }
            else if ( bMinimized ){             // アクティブ時(最小化)
                gPause = TRUE;
            }
            else{
                gPause = FALSE;                 // アクティブ時(通常)
            }
        }
        CASE WM_ENDSESSION:         PostMessage( hWnd, WM_CLOSE, 0, 0 );
        CASE WM_LBUTTONDOWN:        SendMessage( hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0 );
        CASE WM_LBUTTONDBLCLK:      SetWindowPos( hWnd, HWND_TOPMOST,   0, 0, 0, 0, (SWP_NOMOVE|SWP_NOSIZE) );
        CASE WM_RBUTTONDBLCLK:      SetWindowPos( hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, (SWP_NOMOVE|SWP_NOSIZE) );
        CASE WM_ERASEBKGND:         return 1;       /* 何も処理しない⇒塗り潰した */
        DEFAULT:                    return DefWindowProc( hWnd, uMsg, wParam, lParam );
    }
    return 0;
}

//------------------------------------------------
// ウインドウ・クラスの登録
//------------------------------------------------
static ATOM funcWindowClass( HINSTANCE hInstance, LPCTSTR lpClassName )
{
    WNDCLASSEX wcex = { 0 };
    
    wcex.cbSize         = sizeof(WNDCLASSEX);
    wcex.style          = CS_DBLCLKS;
    wcex.lpfnWndProc    = mainWindowProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon( NULL, IDI_APPLICATION );
    wcex.hIconSm        = LoadIcon( NULL, IDI_APPLICATION );
    wcex.hCursor        = LoadCursor( NULL, IDC_ARROW );
    wcex.hbrBackground  = (HBRUSH)GetStockObject( NULL_BRUSH );
    wcex.lpszMenuName   = NULL;
    wcex.lpszClassName  = lpClassName;
    return RegisterClassEx( &wcex );
}

//------------------------------------------------
// ウインドウ・サイズの設定
//------------------------------------------------
static VOID funcSetClientSize( HWND hWnd, LONG sx, LONG sy )
{
    RECT rc1;
    RECT rc2;
    
    GetWindowRect( hWnd, &rc1 );
    GetClientRect( hWnd, &rc2 );
    sx += ((rc1.right - rc1.left) - (rc2.right - rc2.left));
    sy += ((rc1.bottom - rc1.top) - (rc2.bottom - rc2.top));
    SetWindowPos( hWnd, NULL, 0, 0, sx, sy, (SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_NOMOVE) );
}

//------------------------------------------------
// ウインドウの作成
//------------------------------------------------
static HWND funcCreateWindow( HINSTANCE hInstance, LPCTSTR lpClassName, LPCTSTR lpTitleName, int nCmdShow )
{
    gWnd = CreateWindowEx(
        0,                  // 拡張ウインドウ・スタイル
        lpClassName,        // ウインドウのクラス名
        lpTitleName,        // ウインドウのタイトル名
        WINDOW_STYLE,       // ウインドウのスタイル
        CW_USEDEFAULT,      // ウインドウの横軸位置
        CW_USEDEFAULT,      // ウインドウの縦軸位置
        CW_USEDEFAULT,      // ウインドウの横幅
        CW_USEDEFAULT,      // ウインドウの高さ
        NULL,               // 親ウインドウのハンドル
        NULL,               // メニューバーのハンドル
        hInstance,          // インスタンスのハンドル
        NULL );             // ウインドウ作成のデータ
    
    if ( gWnd != NULL ){
        funcSetClientSize( gWnd, gWidth, gHeight );
        ShowWindow( gWnd, nCmdShow );
        UpdateWindow( gWnd );
    }
    return gWnd;
}

//--[ライブラリ関数]------------------------------------------------------------

//------------------------------------------------
// 記号定数(エラーコード)
//------------------------------------------------
#define ERRCODE_SUCCESS     (0)
#define ERRCODE_SCREEN      (-1)
#define ERRCODE_WINREG      (-2)
#define ERRCODE_CREATE      (-3)

//------------------------------------------------
// 記号定数(エラー文字列)
//------------------------------------------------
#define ERRMSG_SCREEN       TEXT("スクリーン・サイズが設定できません。")
#define ERRMSG_WINREG       TEXT("ウインドウ・クラスが登録できません。")
#define ERRMSG_CREATE       TEXT("ウインドウが作成できません。")
#define ERRMSG_FATAL        TEXT("不明なエラー・コードがセットされてます。(%ld)")

//------------------------------------------------
// メモリ・デバイスコンテキストの作成
//------------------------------------------------
extern HDC GameMainMemDC( HWND hWnd, LONG nWidth, LONG nHeight )
{
    HDC         hDC;
    HDC         hMemDC;
    HBITMAP     hBitmap;
    
    // DCコンパチブルの作成
    hDC         = GetDC( hWnd );
    hMemDC      = CreateCompatibleDC( hDC );
    hBitmap     = CreateCompatibleBitmap( hDC, nWidth, nHeight );
    SelectObject( hMemDC, hBitmap );
    SelectObject( hMemDC, GetStockObject(DC_PEN) );
    SelectObject( hMemDC, GetStockObject(DC_BRUSH) );
    DeleteObject( hBitmap );
    ReleaseDC( hWnd, hDC );
    return hMemDC;
}

//------------------------------------------------
// ゲームのウインドウを初期化
//------------------------------------------------
extern BOOL GameMainInit( LONG nWidth, LONG nHeight, UINT nFPS )
{
    if ( gWnd == NULL ){
        gWnd        = NULL;
        gFPS        = nFPS;
        gPause      = FALSE;
        gWidth      = nWidth;
        gHeight     = nHeight;
        gErrCode    = ERRCODE_SUCCESS;
        return TRUE;
    }
    gErrCode = ERRCODE_SCREEN;
    return FALSE;
}

//------------------------------------------------
// ゲームのウインドウを作成
//------------------------------------------------
extern BOOL GameMainCreate( HINSTANCE hInstance, LPCTSTR lpClassName, LPCTSTR lpTitleName, INT nCmdShow )
{
    // ウインドウ・クラスの登録
    if ( funcWindowClass(hInstance,lpClassName) == 0 ){
        gErrCode = ERRCODE_WINREG;
        return FALSE;
    }
    // ウインドウの作成
    if ( funcCreateWindow(hInstance,lpClassName,lpTitleName,nCmdShow) == NULL ){
        gErrCode = ERRCODE_CREATE;
        return FALSE;
    }
    return TRUE;
}

//------------------------------------------------
// ゲームのメインループを処理
//------------------------------------------------
extern LONG GameMainRun( GAMEFUNC fnInit, GAMEFUNC fnTerm, GAMEFUNC fnUpdate, GAMEDRAW fnDraw )
{
    MSG         Msg;
    TIMECAPS    Caps;
    DWORD       dwTime;
    HDC         hMemDC;
    
    // ゲームの初期化
    fnInit( gWnd );
    timeGetDevCaps( &Caps, sizeof(TIMECAPS) );
    timeBeginPeriod( Caps.wPeriodMin );
    dwTime = timeGetTime();
    hMemDC = (HDC)GetWindowLong( gWnd, GWL_USERDATA );
    
    for ( ; ; ){
        if ( PeekMessage(&Msg,NULL,0,0,PM_NOREMOVE) ){
            if ( GetMessage(&Msg,NULL,0,0) <= 0 ){
                break;
            }
            TranslateMessage( &Msg );
            DispatchMessage( &Msg );
        }
        else if ( gPause ){         // ゲームの一時停止
            dwTime = timeGetTime();
            Sleep( 1 );
        }
        else if ( (timeGetTime() - dwTime) >= (1000 / gFPS) ){
            dwTime = timeGetTime();
            PatBlt( hMemDC, 0, 0, gWidth, gHeight, BLACKNESS );
            fnUpdate( gWnd );       // ゲームの進行
            fnDraw( hMemDC );       // ゲームの描画
            InvalidateRect( gWnd, NULL, FALSE );
            UpdateWindow( gWnd );
        }
        else{
            Sleep( 1 );
        }
    }
    // ゲームの後始末
    timeEndPeriod( Caps.wPeriodMin );
    fnTerm( gWnd );
    gWnd = NULL;
    return Msg.wParam;
}

//------------------------------------------------
// ゲームのエラー文字列を表示
//------------------------------------------------
extern LONG GameMainErrMsg( HWND hWnd, LPCTSTR lpTitle )
{
    TCHAR   szBuff[ 128 ];
    LPTSTR  lpForm;
    UINT    nStyle;
    
    switch ( gErrCode ){
        CASE ERRCODE_SUCCESS:   return 0;
        CASE ERRCODE_SCREEN:    lpForm = ERRMSG_SCREEN; nStyle = (MB_OK|MB_ICONERROR);
        CASE ERRCODE_WINREG:    lpForm = ERRMSG_WINREG; nStyle = (MB_OK|MB_ICONERROR);
        CASE ERRCODE_CREATE:    lpForm = ERRMSG_CREATE; nStyle = (MB_OK|MB_ICONWARNING);
        DEFAULT:                lpForm = ERRMSG_FATAL;  nStyle = (MB_OK|MB_ICONERROR);
    }
    wsprintf( szBuff, lpForm, gErrCode );
    MessageBox( hWnd, szBuff, lpTitle, nStyle );
    return gErrCode;
}

//------------------------------------------------------------------------------
// End of GameMainLib.cpp
//------------------------------------------------------------------------------

関連記事



コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« 付録A ゲームのフレームワー... | トップ | 付録C ゲームソースのテンプ... »
最新の画像もっと見る

コメントを投稿

無料で学べる講座」カテゴリの最新記事