はじめに
前回の続きでウィンドウの生成について、個人的によく使われる(であろうと思っている)ウィンドウスタイルを通して解説していきます。
枠なしウィンドウ
前回のウィンドウ生成にてCreateWindowA
の第3引数にWS_OVERLAPPEDWINDOWを指定しました。
この第3引数はウィンドウのスタイルを定義するためものです。
WS_OVERLAPPEDWINDOWは最も標準的なスタイルで、最小化/最大化/閉じるボタンが有効、かつ、ウィンドウ枠で自在にサイズを変更できます。
しかし、自在にサイズ設定をされては、レイアウトの都合上ゲーム用のウィンドウとしては適していません。
また、ウィンドウの枠線も不要の場合が多々あります。
そこで、前回のソースコードを修正し、サイズ変更不可で枠なしのウィンドウを生成します。
ついでに画面の最前面に表示されるような設定も行います。
// ウィンドウ生成
hwnd = CreateWindowExA(
WS_EX_TOPMOST,
szWinName,
szWinTitle,
WS_VISIBLE | WS_POPUP,
CW_USEDEFAULT, CW_USEDEFAULT,
640, 480,
HWND_DESKTOP,
NULL,
hThisInst,
NULL
);
ウィンドウ生成時に呼び出す関数をCreateWindowExA
に変更しています。
これは、拡張スタイルのウィンドウを生成するためです。
拡張スタイルにあたるものがWS_EX_TOPMOSTの指定です。
これによりウィンドウの位置が最前面になります。
WS_VISIBLE | WS_POPUP,
では、枠なし/タイトルバーなしウィンドウを表示させます。
SetFocus(hwnd);
今回追加したコードです。
キーボード入力を生成ウィンドウに向けます。
さて、これにより枠なしウィンドウを表示できるようになりましたが、閉じるボタンがありません。
このままでは不親切なので、別途アプリの終了を行う処理を用意しましょう。
// キーボード押下
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
break;
ウィンドウ関数に追加したコードです。
case WM_KEYDOWN:
でキー押下状態のイベントを処理します。
wParamには押下されたキーの値がセットされています。
この処理ではESCキーが押下された場合にアプリを終了させます。
メニュー付きウィンドウ
WS_OVERLAPPEDWINDOWのスタイルは標準的であると述べましたが、多くのアプリにあるものが存在しません。
タイトルバーの下にあるメニューです。
メニュー作成には、2つのファイルを事前に用意します。
- メニュー項目を一意に識別する値が定義されたヘッダファイル(*.h)
- メニュー項目が定義されたリソースファイル(*.rc)
*.rcファイルは事前にリソースコンパイルする必要があります。
VSCodeのtasks.jsonのtasks配列に下記の設定を追加してください
(ファイルパスはご自身の環境に合わせて読み替えてください)。
{
"type": "shell",
"label": "C/C++: windres.exe resource compiler",
"command": "C:\\gcc\\mingw64\\bin\\windres.exe",
"args": [
"-i",
"${file}",
"-o",
"${fileDirname}\\${fileBasenameNoExtension}.o"
],
"options": {
"cwd": "C:\\gcc\\mingw64\\bin"
},
"group": "build",
"detail": "compiler: C:\\gcc\\mingw64\\bin\\windres.exe"
}
また、既存のg++.exe
のargsにも、リソースコンパイルしたファイルをリンクするよう修正してください。
"-mwindows",
"-fdiagnostics-color=always",
"-g",
"${file}",
"${fileDirname}\\${fileBasenameNoExtension}.o",
"-o",
"${fileDirname}\\${fileBasenameNoExtension}.exe"
リソースファイルでは、サブメニューを持つ/持たない2種類のメニューを定義しました。
どちらもアプリを終了するだけの簡単なメニューです。
重要な箇所はMENU_EXAMPLE001_002 MENU MENU
とMENUITEM "&Exit", IDM_EXIT
です。
MENU_EXAMPLE001_002はメニューの識別名で、ウィンドウ生成時に使用します。
IDM_EXITはメニュー項目のIDで、ウィンドウ関数のパラメーターとして使用されます。
wcl.lpszMenuName = "MENU_EXAMPLE001_002";
WNDCLASSEXA構造体のlpszMenuName
に、リソースファイルで定義したメニュー識別名を指定することで、メニューをロードします。
// メニュー項目選択
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDM_EXIT:
MessageBoxA(hwnd, "Quit this app.", "EXIT", MB_OK);
PostQuitMessage(0);
break;
}
break;
ウィンドウ関数に追加したコードです。
case WM_COMMAND:
でメニュー項目選択時のイベントを処理します。
wParamには選択されたメニュー項目のIDがセットされています(example001_002b.h
で定義)。
MessageBoxAはダイアログ表示を行う関数です。ダイアログ表示後に終了処理を行います。
※なお、実際において、メニューを定義するrcファイルをテキストで編集することは稀です。
GUIによるリソース用エディタを使用することがほとんどです。
ダブルクリック対応ウィンドウ
ウィンドウにダブルクリック操作を検知させたい場合の処理を解説します。
これまでに生成したウィンドウはダブルクリックの操作に対応していません。
wcl.style = CS_DBLCLKS;
WNDCLASSEXA構造体のstyle
を設定することで、ウィンドウがダブルクリック操作イベントのメッセージを受け取るようになります。
int iResp;
// ダブルクリック(左ボタン)
case WM_LBUTTONDBLCLK:
iResp = MessageBoxA(hwnd, "Quit this app ?", "Confirm", MB_YESNO);
if(iResp == IDYES)
{
PostQuitMessage(0);
}
break;
ウィンドウ関数に追加したコードです。
case WM_LBUTTONDBLCLK:
でダブルクリック時のイベントを処理します。
マウスカーソルがウィンドウ上にある状態でダブルクリックすると、修了確認のダイアログが表示されます。
「はい」を選択するとアプリの終了処理を行います。
次回予告
次回はいよいよ画像の描画について取り扱います。
Windowsアクセサリのペイントで作成可能なBMPファイルを用いて解説していきます。
ゲーム作成にはグラフィックは必須です。
生成したウィンドウ内に画像を表示させましょう。