contents

  1. ソース
  2. 使い方
  3. 制限・仕様
  4. コツ

解像度の切り替えとフルスクリーン

よくゲームで使われる、最大化ボタンを押すと画面解像度を変えて最大化する処理について解説します。 GDI/GDI+などの、DirectXを使わない場合に使用します。

ソース

とりあえずソースを見てみる ⇒ ScreenMode.h ScreenMode.cpp Visual C++ プロジェクト

使い方

使用するにあたり、ソースに書き加える部分は以下の3点です。

コンストラクタ

コンストラクタの宣言は以下のようになっています。

ScreenMode(UINT width, UINT height, UINT minbpp, UINT menuID, UINT notifyMessage = 0);

width, height はフルスクリーンモードでの幅、高さを指定します。 minbpp はフルスクリーンモードでの最低の色数を指定します。 色数は、256色=8, フルカラー=16, トゥルーカラー=24 or 32 です。 menuIDは、画面モードの切り替えに使用するメニューのIDを指定します。 menuID 〜 menuID+100 までが予約されますので、他のメニューと衝突しないようにしてください。 notifyMessage は画面モードが変更された場合に送られるウィンドウメッセージを指定します。 デバイス依存ビットマップ(DDB)などはモードが切り替わると無効(DIBに変換?)になりますので、作り直す必要があります。 (WM_DEVMODECHANGE をフックすればnotifyMessageは必要ないかも)

ウィンドウプロシージャ

ウィンドウプロシージャの先頭で、ScreenModeにメッセージをフックさせてやります。 以下のようなコードを追加してください。

ScreenMode screenMode;

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  LRESULT lResult = 0;
  if(!screenMode.OnMessage(hWnd, msg, wParam, lParam, lResult))
    return lResult;
  // 以下、あなたのコード
  switch(msg)
  {
    ...

メニューへの追加

モード切替メニューを自動的に作成されます。 ウィンドウのメニューに追加するには、以下のようなコードを加えます。 モードが変更されたときには、自動的にメニューも書き換わります。

ScreenMode screenMode;
UINT insertPos;

  case WM_CREATE:
  {
    HMENU hMenu = GetMenu(hWnd);
    InsertMenu(hMenu, insertPos, MF_BYPOSITION | MF_POPUP, (UINT)screenMode.GetModeMenu(), TEXT("設定(&S)"));
    SetMenu(hWnd, hMenu);
    break;
  }
    ...

制限・仕様

解像度は1種類のみ

指定できる解像度は1種類だけです。 これは、GDI/GDI+などを使用するゲームでは、通常、ウィンドウサイズがそのゲームで使用するビットマップのサイズに依存すると考えたためです。 解像度に依存しないゲームとなると、Direct3Dを使ったものになるでしょうから。 (ただ、対応させること自体は難しくありません。)

非アクティブ化時にウィンドウモードに戻る

非アクティブ化時には最小化し、再アクティブ化時にもとのフルスクリーンに戻る実装も考えられますが、 こちらになったのは、単に実装が楽だったためですw

コツ

ゲームが終わると他のウィンドウが小さくなってるよ!

けっこう頻繁に見られる症状です。 これを防ぐには、ChangeDisplaySettings()の第2引数にCDS_FULLSCREENを指定してやります。 ただし、効果があるのは Windows NT/2000/XP だけのようです。 9x系で同様なことを実現するためには、デスクトップのウィンドウを数え上げ、位置をすべて保存し、自前で戻してやる必要があります。 ……めんどくさい。

固定サイズウィンドウについて

画面モードの切り替えとは直接関係ありませんが、サイズ変更のできないウィンドウの作り方です。 ウィンドウスタイルの指定と、指定したクライアント領域の大きさを持つウィンドウを作成するには以下のようにします。

const DWORD WS_FIXEDWINDOW    = WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
const DWORD WS_EX_FIXEDWINDOW = WS_EX_OVERLAPPEDWINDOW; // こちらはお好みで

// BOOL hasMenu;
// INT  width, height;

RECT rc = {0, 0, width, height};
AdjustWindowRectEx(&rc, WS_FIXEDWINDOW, hasMenu, WS_EX_FIXEDWINDOW);
HWND hWnd = CreateWindowEx(WS_EX_FIXEDWINDOW, windowName, className, WS_FIXEDWINDOW,
  CW_USEDEFAULT, 0, rc.right - rc.left, rc.bottom - rc.top,
  NULL, NULL, hInstance, NULL);

これはウィンドウが1つだけの場合の処理ですが、ビューウィンドウを使用する場合はもう少し複雑になります。 (いったん作成してみて、クライアントのサイズの差分だけ親ウィンドウのサイズを変更するしかないのでは?)


2002/05/10