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点です。

  1. エントリーポイントの違い(main or WinMain)
  2. GetMessage と PeekMessage の違い
  3. 文字コード

 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を使うべきと言われました。 つらいですね。