ここまでの冒険を記録しますか?

長岡技大ソフトウェア開発サークルの開発ブログ

DXライブラリとWindowsAPIの連携その3【ダイアログボックス】

書き手:肥田野

少し間が空きましたが、今回はDXライブラリの環境でダイアログボックスを使う方法に付いて解説していきます。

もう気付いている人も多いかもしれませんが、この連載の存在意義の半分以上は、第1回の準備編にあるんです。

第1回でDXライブラリでもウィンドウプロシージャが使えると知ってもらえたら、あとは数多に存在するWindowsAPIの解説をされているサイトや専門書を読んで頂ければ、自分で応用は出来ると思います。

なので今回のダイアログボックスについても、説明する内容は一般的なWindowsAPIの使い方とほぼ変わりません。

このページに訪れた方はおそらくDXライブラリでゲームを作りたいという方がほとんどだと思いますので、今回のお話もゲーム作りにどう役立てていけるかを想像しながら読んでもらえればなと思います。

それでは具体的な説明へ移りましょう。

前回はリソースという概念を解説し、その代表格としてメニューバーを実装しました。

ダイアログボックスもリソースの一種で、リソースビューから「プロジェクト名.rc」を右クリック、「リソースの追加」をクリックして「Dialog」を選択します。Dialogの中から細かい種類が選べるようになっていますが、今回は上位の「Dialog」を選択して、「新規作成」をクリックしてください。

あ、今出てきた小さなウィンドウもダイアログボックスですよ。ダイアログボックスは自分で表示する項目(リストボックスやボタンなど)を自由に編集できますが、今回はとりあえず初期状態の「OK」「キャンセル」ボタンだけ扱うことにします。リストボックスやボタンといった細かいツールの使い方は次回解説したいと思います。

ダイアログボックスが開いたら、ダイアログボックスのID(おそらくIDD_DIALOG1だと思います)とボタンのID(IDOK、IDCANCEL)を確認してください。

ダイアログボックスはメニューのボタンが押された時に出てきた方が「それっぽい」ですよね。なので前回作ったメニューにボタンを追加します。

既に存在する「終了」ボタンを右クリックすると、「新しい項目を挿入」が選べると思いますので、それで終了ボタンの上に項目を追加します。

テキストは「ダイアログ(&D)」、IDは「IDM_DIALOG」でよいでしょう。

それではソースコードの編集です。

#include<DxLib.h>
#include"resource.h"


WNDPROC dxWndProc;
HWND hMainWnd;
//インスタンスハンドルを格納するグローバル変数
HINSTANCE hInst;

int SettingBeforeInit(bool);
int SettingAfterInit();
LRESULT CALLBACK MyProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);

BOOL CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) {
    //インスタンスハンドルの代入
    hInst = hInstance;
    SettingBeforeInit(true);
    DxLib_Init();
    SettingAfterInit();

    while (!ProcessMessage() && !ScreenFlip() && !ClearDrawScreen()) {

    }
    DxLib_End();
    return 0;
}

//SettingBeforeInit()、SettingAfterInit()は変更しないので省略

LRESULT CALLBACK MyProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
    switch (msg) {
    case WM_COMMAND:
        switch (LOWORD(wp)) {
            //ダイアログメニューが選択された時の処理
        case IDM_DIALOG:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, MyDlgProc);
            break;
        case IDM_END:
            SendMessage(hWnd, WM_CLOSE, NULL, NULL);
            break;
        }
        break;
    }
    return CallWindowProc(dxWndProc, hWnd, msg, wp, lp);
}

BOOL CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) {
    switch (msg)
    {
    case WM_COMMAND:
        switch(LOWORD(wp)) {
        case IDOK:
            EndDialog(hDlg, IDOK);
            return TRUE;
        case IDCANCEL:
            int answer = MessageBox(hDlg, "設定を変更せずにダイアログを閉じます", "確認", MB_OKCANCEL);
            if (answer == IDOK) {
                EndDialog(hDlg, IDOK);
            }
            return TRUE;
        }
        return FALSE;
    }
    return FALSE;
}

まずダイアログボックスを表示するためには、インスタンスハンドルというものが必要になります。

ウィンドウハンドルに似ていますが、あちらはウィンドウごとに別々に存在するのに対し、インスタンスハンドルはプログラムに一つです。

インスタンスハンドルはWinMainが実行される際にOSから渡されるので、それをグローバル変数に格納しています。

ちなみにWinMainの引数にHINSTANCE型が二つありますが、ここで使うのは一つ目のhInstanceです。参考書によってはhCurInstanceと書かれているかもしれません。CurとはCurrent(=現在の)を意味し、変数名などでよく使われる略語です。この機会に覚えておきましょう。

そして一つ目のHINSTANCEが現在なら、二つ目は以前です。hPrevInstanceのPrevは「Previous」から来ています。しかし、この変数はOSが16bitの時代までしか使われておらず、現在の32bit、64bitの世界では常にNULLになっていて、互換性維持の為にしか存在していないそうです。

ちょっと脇道にそれました。インスタンスハンドルを格納したらWinMainの変更は終わりです。MyProcでメニューバーの振る舞いを変更させましょう。

前回作ったIDM_ENDの上に、IDM_DIALOGを追加しました。中身はシンプルで、DialogBoxマクロを呼び出すだけです。そう、これも関数でなくマクロなんです。カーソルをあててマクロの変換元を辿っていくと、元の関数(DialogBoxParamA)にたどり着きます。この辺の行き着く先がバージョンによって異なるのかもしれませんね。

ダイアログボックスの初期状態にはOKとキャンセルの二つのボタンがあり、IDはそれぞれIDOKとIDCANCELでしたね。これはどこで受け取るのかというと、ダイアログボックス用の新しいプロシージャを作る必要があります。今回はMyDlgProcと名付けました。ソースコードの上の方でプロトタイプ宣言するのを忘れないようにしてください。

基本的な構成はMyProcと同じなのですが、MyProcは返り値がCallWindowProcだったのに対しMyDlgではTRUEかFALSEになります。IDOK、IDCANCELなど自分の用意した項目に反応したときはTRUE、何もアクションが起きなかったときはFALSEを返します。

ダイアログボックスを閉じるときはEndDialog関数を呼び出します。どちらのボタンを押しても結果が変わらないのはつまらないので、キャンセルを押したときはメッセージウィンドウで確認を取ることにしました(そもそもまだ設定する項目を作っていないのですが……(^_^;))

これでダイアログボックスが実装できました。今のところテキストの一つも表示せずにOKとキャンセルのボタンだけが隅に控えているという、メッセージボックス以下の機能しかありませんが、次回からここに色々足していきたいと思います。

ゲームにおけるダイアログといえば、私はまずコンフィグを想像しますね。キー操作を変更したり、音量を調節したり。そういえば「充実したオプション」なんてネタがありましたね……気になる人はググってみてください。

それでは今回はこの辺で。