課題7

 ウィンドウモードへの対応。

前準備

  1. 課題7用基本プログラムをダウンロードし、解凍する。
  2. 課題6用のプロジェクトを作成し、ダウンロードしたすべてのファイルをプロジェクトに追加する。
    (リソースはすでに作ってあるため、リソースの新規作成は必要ない)
  3. プロジェクトの設定(ddraw.lib, dxguid.lib, winmm.lib)を行い、コンパイル、実行する。

設問a

フルスクリーンモードで作成されているプログラムを、 ウィンドウモードで動くよう修正する。
注)設問bでフルスクリーンモードの切り替えを行うため、myDraw.cpp内の変更点は変更前の状態をコメントアウトしておくとよい。

手順1.Winmain.cppを変更する。

変更1.グローバル変数にウィンドウのハンドルを設定する。(InitializeWindow関数内のウィンドウハンドルの変数をグローバル変数にする。)

//----------------------------------------------------------------
// グローバル変数 Global data
//----------------------------------------------------------------
HWND hWnd;

変更2.CalcScreenRect(HWND,LPRECT);を追加。

//-----------------------------------------------------------------------------
// 関数名 : CalcScreenRect
// 機能概要: クライアント領域のスクリーン座標を求める
//-----------------------------------------------------------------------------
void CalcScreenRect(HWND hWnd,LPRECT client_rect)
{
    POINT client_point;

    // クライアント領域の左上座標のスクリーン座標を求める
    client_point.x = client_point.y = 0;
    ClientToScreen(hWnd, &client_point);

    // クライアント領域のサイズを求める
    GetClientRect(hWnd, client_rect);

    // クライアント領域のスクリーン座標を求める(client_rectに座標が計算される)
    OffsetRect(client_rect, client_point.x, client_point.y);
}

変更3.WinProc()関数にWM_SIZEの処理を追加

//-----------------------------------------------------------------------------
// 関数名 : WinProc()
// 機能概要: ウィンドウプロシージャ
//-----------------------------------------------------------------------------
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    RECT rect;

    switch (message)
    {
        case WM_SIZE: // ウィンドサイズを一定に保つ処理
            GetWindowRect(hWnd, &rect);
            rect.right -= LOWORD(lParam);
            rect.bottom -= HIWORD(lParam);
            rect.right += 640;
            rect.bottom += 480;
            MoveWindow(hWnd,rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
            break;
        case WM_KEYDOWN:
            〜以下略〜
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

手順2.common.hに追加

extern HWND hWnd;
void CalcScreenRect(HWND,LPRECT); // クライアント領域のスクリーン座標を取得

手順3.myDraw.cppを変更

変更1.ローカルデータにスクリーンモード格納用変数を作成

//-----------------------------------------------------------------------------
// ローカルデータ Local data
//-----------------------------------------------------------------------------
static DWORD ncoop = DDSCL_NORMAL;// 現在のスクリーンモード格納用変数
		

変更2.myFlip内を変更する

void myFlip(void)
{
    HRESULT hRet;
    RECT rect1, rect2;

    if(ncoop == DDSCL_NORMAL)
    {
        CalcScreenRect(hWnd, &rect1);
        SetRect(&rect2, 0, 0, 640, 480);
        hRet = g_pDDSPrimary->Blt(&rect1, g_pDDSBack, &rect2, DDBLT_WAIT, NULL);
        if(hRet == DDERR_SURFACELOST)
        {
            g_pDDSPrimary->Restore();
            g_pDDSBack->Restore();
        }
    }
    else
        g_pDDSPrimary->Flip(NULL, DDFLIP_WAIT);
}

変更3.InitializeDraw内を変更する

// 協調レベルの設定(ウィンドウモード用)
hRet = g_pDD->SetCooperativeLevel(NULL, DDSCL_NORMAL);

// ビデオモードの設定 640x480x8(ウィンドウモードの時は必要ない)
if(ncoop != DDSCL_NORMAL)
{
    hRet = g_pDD->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, COLOR_MODE, 0, 0);
    if (hRet != DD_OK)
        return InitFail(hWnd, hRet, "SetDisplayMode FAILED");
    OutputDebugString("SetDisplayMode OK\n");
}

// プライマリーサーフェイスの生成
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
//ddsd.dwBackBufferCount = 1; // ウィンドウモードの時は必要ない

// バックバッファの生成
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth = 640;
ddsd.dwHeight = 480;
hRet = g_pDD->CreateSurface(&ddsd,&g_pDDSBack,NULL);
if (hRet != DD_OK)
    return InitFail(hWnd, hRet, "GetAttachedSurface FAILED");
OutputDebugString("GetAttachedSurface OK\n");

// クリッパーの設定(ウィンドウモードの時必要)
if (ncoop == DDSCL_NORMAL)
{
    g_pDD->CreateClipper(0,&g_pDDClip, NULL);
    if(g_pDDClip->SetHWnd(0,hWnd) != DD_OK)
        return NULL;
    if(g_pDDSPrimary->SetClipper(g_pDDClip) != DD_OK)
        return NULL;
}
if (ncoop != DDSCL_NORMAL)
{
    // パレットの生成(指定した画像を元に、パレット情報を作成する)
    g_pDDPal = DDLoadPalette(g_pDD, szBackimgBmp);
    if (g_pDDPal == NULL)
        return InitFail(hWnd, hRet, "DDLoadPalette FAILED");
    OutputDebugString("DDLoadPalette OK\n");
    // プライマリー・サーフェイスにパレットを設定する。フルスクリーンのみ設定必要
    hRet = g_pDDSPrimary->SetPalette(g_pDDPal);
    if (hRet != DD_OK)
        return InitFail(hWnd, hRet, "SetPalette FAILED");
    OutputDebugString("SetPalette OK\n");
}

[実行結果サンプル]

設問b

ゲーム中にウィンドウモードとフルスクリーンモードをF11を押すたびに切り替えれるよう、 プログラムを改造する。

手順1

F11が押されるたびに呼び出されるモード切り替えるよう関数ChangeScreen()を作成する。(myDraw.cpp内)
この関数のプロトタイプはcommon.h内に記述する。(Winmain.cpp内で使用するため)

//-----------------------------------------------------------------------------
// 関数名 : ChangeScreen()
// 機能概要: 文字列表示 ポイント数、フォントの種類指定版
//-----------------------------------------------------------------------------
BOOL ChangeScreen(void)
{
    if (ncoop & DDSCL_FULLSCREEN) // フル−>ウィンドウ
    {
        ShowCursor(true);
        if(DD_OK != g_pDD->RestoreDisplayMode())
            return FALSE; // 解像度を元に戻す
ncoop = DDSCL_NORMAL; } else // ウィンドウ−>フル { ShowCursor(false); ncoop = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT; } if(ncoop & DDSCL_EXCLUSIVE) { if(!SetWindowLong(hWnd, GWL_STYLE, WS_POPUP)) // フルスクリーンにあったウィンドウにする return FALSE; } if(g_pDD->SetCooperativeLevel(hWnd,ncoop) != DD_OK) // 強調 return FALSE; if(ncoop & DDSCL_EXCLUSIVE) // ディスプレイモードの変更・フルスクリーンにするときのみ { if(g_pDD->SetDisplayMode(640, 480, 8, 0, 0)!=DD_OK) return FALSE; } else { // ウィンドウモードにあったウィンドウに再定義する if (!SetWindowLong(hWnd, GWL_STYLE, WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_BORDER|WS_MINIMIZEBOX|WS_VISIBLE)) return FALSE; RECT rect = {0, 0, 640, 480}; // GetWindowStyle() <windowsx.h>が必要 AdjustWindowRect(&rect, GetWindowStyle(hWnd), GetMenu(hWnd) != NULL); // ウィンドウを描画する位置をrect構造体へ入れます。 // ↓:ウィンドウ表示座標をセット
SetWindowPos(hWnd, NULL, 0, 0, (rect.right - rect.left), (rect.bottom - rect.top), SWP_NOMOVE|SWP_NOZORDER | SWP_NOACTIVATE); SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); }
InitializeDraw(hWnd); // サーフェイスをセットし直す
return TRUE;
}

手順2

Winmain.cpp内でF11が押されたら、 ChangeScreen()関数を呼び出すように設定する。

手順3

設問aで設定したスクリーンモード格納用変数(ncoop)の値によってmyDraw.cpp内の設定をウィンドウモード用、フルスクリーンモード用の切り替えの設定をする。

[例]

if(ncoop == DDSCL_NORMAL)
{
  ウィンドウモード用の設定
}
else
{
  フルスクリーンモード用の設定
}

ヒント1

DirectDrawオブジェクトの作成、協調レベルの設定はInitializeDraw関数内では1回だけ通るようにする。(ChangeScreen関数内でサーフェイスをセットし直すときには処理をさせない)また、InitializeDraw関数がChangeScreen()関数内から呼ばれるときは、プライマリーサーフェイスを開放(Release)し、NULLをセットする。

ヒント2

プライマリーサーフェイス、バックバッファの設定はウィンドウ用とフルスクリーン用で違うので設定を切り替えるプログラムが必要。

[実行結果サンプル]


[ TOP ]