はじめに
本記事では、簡単なゲーム制作を通じ、C++を用いたWindowsプログラミングの基礎を解説します。
下記のような方を対象としています。
- ゲームを作りたくてプログラミングを独習してみたが、どう実践に活かせばいいのか悩んでいる
- Windows用のアプリを作成したく、その取っ掛かりがほしい
前提として、C/C++の基礎知識があることを想定しています。
開発環境の準備
ダウンロード
下記のサイトからWindows向けの"Visual Studio Code"(以降VSCode)をダウンロードしてください
(本記事ではバージョン1.99.3を使用しています)。
インストール後にすること
VSCodeを起動したら、左部のExtensions
をクリックします。
"C/C++"を検索し、インストールすれば準備完了です。
ウィンドウ生成とそのコード
初めてのプログラミングといえば「Hello World」を出力することかと思います。
しかし、Windowsでは出力するためのウィンドウがなければ始まりません。
そのため、初回はウィンドウを作成することから取り扱います。
上のサンプルコードをご自身のVSCodeにて記述してみてください。
ビルドと実行の手順
ソースコードの解説の前に、まずはビルドして実行してみましょう。
VSCodeの上部メニューTerminal
からRun Build Task
をクリックします。
ビルドタスク選択ポップアップの右にある歯車アイコンをクリックして、tasks.jsonを編集します。
args
に-mwindows
オプションを追加してください。
{
"version": "2.0.0",
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++.exe build active file",
"command": "C:\\gcc\\mingw64\\bin\\g++.exe",
"args": [
"-mwindows",
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}\\${fileBasenameNoExtension}.exe"
],
"options": {
"cwd": "C:\\gcc\\mingw64\\bin"
},
"problemMatcher": [
"$gcc"
],
"group": "build",
"detail": "compiler: C:\\gcc\\mingw64\\bin\\g++.exe"
}
]
}
tasks.jsonの編集後に、もう一度Run Build Task
をクリックし、ビルドを行います。
ビルドに成功すると、cppファイルを保存したフォルダーと同じ場所にexeファイルを作成されますので、実行してみましょう。
黒いウィンドウが表示されるはずです。
今はただこれだけですが、これがWindowsプログラムの第一歩です。
解説
Windowsプログラミングにおいて、最小構成でコーディングを行っても、2つの関数を用意する必要があります。
WinMain関数とウィンドウコールバック関数です。
ここでの解説は、この2つの関数の役割までに留めます。
引数や構造体等の詳細は、次回以降、必要に応じて解説していきます。
WinMain関数
WinMain関数は、エントリーポイントとなる関数です。C/C++におけるmain関数に相当します。
この関数内でやっていることは以下になります。
- ウィンドウクラスを定義する
- ウィンドウを生成する
- OSからのメッセージを読み込む
ウィンドウクラスを定義する
これからプログラムで扱うウィンドウについて、どういうウィンドウを生成するのかを定義します。
WNDCLASSEXA構造体の各メンバに値をセットすることで定義していきます。
ウィンドウを生成する
定義を済ませたウィンドウクラスを表示するには、生成する必要があります。
CreateWindowA関数をコールすることで生成します。
特に重要なのは、第1引数として指定するウィンドウクラス名です。
ここに指定する文字列は、WNDCLASSEXAのlpszClassNameと同じ名前を指定しなければなりません。
const char szWinName[] = "ExampleWin";
WNDCLASSEXA wcl;
wcl.lpszClassName = szWinName;
hwnd = CreateWindowA(
szWinName,
OSからのメッセージを読み込む
Windowsプログラミングは、OSから送信されるメッセージに対して、何かしらのアクションを取る必要があります。
OSからのメッセージとは「ウィンドウズ上でマウスがクリックされた」とか「閉じるボタンが押下された」などです。
イベント駆動のプログラミング経験がないと、少し戸惑うかもしれませんが、OSとプログラムの対話によって実行される処理があります。
PeekMessage関数でOSからのメッセージの有無をチェックし、GetMessageで読み込みます。
if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if(!GetMessage(&msg, NULL, 0, 0))
{
return msg.wParam;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
読み込んだメッセージは、後述のウィンドウコールバック関数が受領し、何かしらの対処を行います。
ウィンドウコールバック関数は、WNDCLASSEXAのlpfnWndProcにて定義された関数を指します。
LRESULT CALLBACK WindowFunc(HWND, UINT, WPARAM, LPARAM);
WNDCLASSEXA wcl;
wcl.lpfnWndProc = WindowFunc;
ウィンドウコールバック関数
サンプルのコードにて、WindowFuncの名前で定義された関数(名前は自由に付けられます)がウィンドウコールバック関数です。
ウィンドウコールバック関数は、LRESULT CALLBACKとして定義する必要があります。
LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
// プログラム終了
case WM_DESTROY:
PostQuitMessage(0);
break;
// その他メッセージ処理
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
この関数はOSから送信されたメッセージごとに、どのような処理を行うか定義します。
OSが送信してくるメッセージの種類は非常に多いため、すべてのメッセージに対してアプリ固有の対応処理を定義することは困難です。しかし、その必要はありません。DefWindowProc関数を呼ぶことで、OSが善きに計らって対処してくれます。
サンプルではWM_DESTROYメッセージに対してのみ明示的に処理を定義しています。
このメッセージはユーザーがアプリを終了しようとすると送信され、PostQuitMessage関数によってプログラムの終了を進めていきます。
WM_DESTROY以外のメッセージにつきましては、次回以降に必要に応じて取り扱っていく予定です。
次回予告
ウィンドウの表示がめでたく無事にできました。
しかし、冒頭で述べたようにゲームを目的としたものです。そのウィンドウが、サイズを自由に変更できてしまうのは望ましくありません。表示位置も起動のたびにランダムというのも不親切です。
次回はこのへんを改善しながら、ウィンドウの生成について、もう少し詳細な説明をしていきます。