Direct3D12アプリを書いてみる(2)
まず、Direct3D12のプログラムを説明する前に、Windowだけを表示するプログラムを書きます。 基本的には、CreateWindowでWindowを生成し、メインループでメッセージを処理するだけです。
ちなみに、Windowのアプリは、メッセージドリブンで動いています。例えば、Windowを移動させたいとか、サイズを変更したいとか、閉じたいとか、そういう操作系はメッセージという形で キューに積まれていき、プログラムはメインループの中でそのメッセージを処理します。そのため、メッセージがないときは、プログラムは、Sleepしているのと同じ状態です。 これは、ゲームプログラムのように、アニメーションなどの処理を、毎フレームリアルタイムに処理するプログラムには適していません。
#include <windows.h>
// メッセージの処理
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_DESTROY) {
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// Windowの生成
WNDCLASSEX wcex = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, hInstance, nullptr, nullptr, nullptr, nullptr, "D3D12SampleWindowClass", nullptr };
RegisterClassEx(&wcex);
HWND hWnd = CreateWindow(wcex.lpszClassName, "Direct3D 12 Sample", WS_OVERLAPPEDWINDOW, 100, 100, 800, 600, nullptr, nullptr, wcex.hInstance, nullptr);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// メインループ
MSG msg = {};
while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
UnregisterClass(wcex.lpszClassName, hInstance);
return 0;
}
シンプルですが、注意することは以下の3点です。
- エントリーポイントの違い(main or WinMain)
- GetMessage と PeekMessage の違い
- 文字コード
1. エントリーポイントの違い(main or WinMain)
main関数だろうが、WinMain関数だろうが、Windowを表示することはできるのですが、Windowsアプリを書くときはWinMain関数を
使うのが一般的だと思います。VisualStudioでビルドをしたときに、main関数とWinMain関数を切り替えたいときは、
「リンカー」→「システム」→「サブシステム」の設定を「Windows」と「コンソール」で切り替えられます。
2. GetMessage と PeekMessage の違い
Window生成のサンプルでは、GetMessage関数が使われることが多いと思います。GetMessage関数は、メッセージがないときは、Sleepしてくれる関数です。 PeekMessage関数はSleepせずに即時で戻ってくる関数です。Direct3Dのプログラムは、毎フレーム描画する必要がありますので、メッセージがなくても、メインループを回す必要があります。 そのため、PeekMessage関数を使っています。
3. 文字コード
WindowsのAPIは標準で文字コードをUTF16で入力するようになっています。もう何十年前からです。これは結構扱いにくいです。 そのため、私は、Windowsアプリを書くときは、「構成プロパティ」→「詳細」→「文字セット」で「マルチバイト文字セットを使用する」という設定に変更して、 ASCIIコードで入力しています。ちなみに、マルチバイト文字セットは、日本語の場合、デフォルトでは、SJISになるようです。
一般に、プログラムを書く文字コードは、UTF8(BOMなし)が使われることが多く、ASCIIコードと互換性があることがメリットです。 ゲームプログラムでも、UTF8が使われることが多いと思います。 UTF8で書いた文字列を使って、UTF16のAPIを使うためには、リアルタイムに変換する必要があります。
しかし、ChatGPT先生に聞いたところ、WindowsのAPIはマルチバイトではなく、UTF16を使うべきと言われました。 つらいですね。