【3Dゲーム開発#01】Windowの表示

前回まででmain関数(エントリポイント)を作成して実行できるようになりました。

現状では何もせずは即終了してしまいます。

今回は、Windowを表示して、即終了せずにユーザー操作で終了できるようにしていきます。

前回の記事:

【3Dゲーム開発#00】VisualStudioプロジェクトの作成
こちらでは、3Dゲーム開発のための技術・実装方法をまとめていきます。まずは3Dゲーム開発の最も主要な開発環境であるVisual Studioでのプロジェクト用意をしていきます。3Dゲーム開発記事のまとめ:「3Dゲーム開発」WindowsPC...

3Dゲーム開発記事のまとめ:「3Dゲーム開発」

アプリの雛形作成

まずmain関数内でごちゃごちゃ書くよりも、制作アプリの処理を分離できるようにApplicationクラスを作成して、main関数から呼び出すことで、すっきりわかりやすい雛形を作りましょう。

Main関数を変更

まずは全体構造がわかりやすいように、Main関数から構成を見ていきましょう。使用するクラスの詳細な実装は後述していきます。

Main関数を以下のように、Applicationクラスで、初期化 → ループ → 終了という形で構成します。

#include <Windows.h>
#include "Application.h"
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, int)
{
    Application* application = new Application();
    // アプリ初期化
    application->Init(hInst);

    // ループ : アプリメインの実装を書くところ
    application->Loop();

    // アプリ終了処理
    application->Terminate();

    delete(application);

    return 0;
}

こんな形にすることで、main関数自体はすっきりと、制作アプリに必要な実装はApplicationクラス内で、といったわかりやすい雛形ができます。

Applicationクラス

上記で呼び出しているApplicationクラスはひとまず以下のように実装します。

冒頭でも述べたとおり、まずWindowsアプリで必須のWindow表示を行うので、後述するWindowクラスも持たせています。

#pragma once
#include "Window.h"

class Application
{
public:
    void Initialize(HINSTANCE hInst);
    void Loop();
    void Terminate();

private:
    // アプリのメインループ
    bool gameLoop();

private:
    Window  window_;
};
#include "Application.h"

void Application::Initialize(HINSTANCE hInst)
{
    window_.Initialize(hInst);
}

void Application::Loop()
{
    while (true) {
        bool result = window_.MessageLoop();
        if (result == false) break;

        result = gameLoop();
        if (result == false) break;
    }
}

void Application::Terminate()
{
    window_.Terminate();
}

bool Application::gameLoop()
{
    return true;
}

Loop関数でwhileループとなり、ここがメインのループ処理になります。

Windowクラスの実装は後述ではありますが、Windowも初期化、ループ、終了処理を呼んでいまず。

そして、Loop関数内の gameLoop() がこれからゲーム実装をしていくメインのゲームループとなります。

Windowクラス

以下がWindowsの「ウィンドウ」を処理するWindowクラスです。

Windowsのウィンドウ処理はWinAPIを使った定型的な処理です。

あまり深堀りしても意味はないので、基本的にはこう書けばWindowが表示されて、Windowsアプリの土台となるウィンドウ表示ができる、と思っておけば良いでしょう。

WinAPI特有の型や宣言などで分かりづらく感じる部分もありますが、Windowに必要なパラメータと、必要なAPIの呼び出しを行っているだけです。

Windowsアプリケーション自体の実装に興味がある場合、WinAPIやWindowsの実装を調査すると良いでしょう。

ただ、ゲームではない実用系のWindowsアプリなら、C#がフレームワークが豊富でより手軽に実装が可能です、あまりC++でWindowsアプリを掘り下げるのは得策ではないと考えています。

#include <windows.h>

class Window
{
public:
    Window();
    virtual ~Window();

    bool Initialize(HINSTANCE hInst);

    bool Terminate();

    bool MessageLoop();

    static LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

    void OnResize(UINT width, UINT height);

    HWND    GetWindowHandle() { return hWindow_; }

private:
    WNDCLASS    wc_;
    HWND        hWindow_ = nullptr;
    SIZE        sizeWindow_;
    MSG         msg_ = {};
};
#include "Window.h"
#include <tchar.h>

Window::Window()
    : sizeWindow_()
{
    sizeWindow_.cx = 640;
    sizeWindow_.cy = 480;
}

Window::~Window()
{

}

bool Window::Initialize(HINSTANCE hInst)
{
    // ウインドウ クラスの登録
    wc_.style           = CS_HREDRAW | CS_VREDRAW;
    wc_.lpfnWndProc     = (WNDPROC)Window::MainWndProc;
    wc_.cbClsExtra      = 0;
    wc_.cbWndExtra      = 0;
    wc_.hInstance       = hInst;
    wc_.hIcon           = LoadIcon(hInst, IDI_APPLICATION);
    wc_.hCursor         = LoadCursor(hInst, IDC_ARROW);
    wc_.hbrBackground   = GetSysColorBrush(COLOR_WINDOW);
    wc_.lpszMenuName    = nullptr;
    wc_.lpszClassName   = _T("ShowWindow");
    if (!RegisterClass(&wc_)) {
        return false;
    }

    // メイン ウインドウ作成
    RECT rect;
    rect.top    = 0;
    rect.left   = 0;
    rect.right  = sizeWindow_.cx;
    rect.bottom = sizeWindow_.cy;
    AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);

    hWindow_ = CreateWindow(wc_.lpszClassName, _T("ShowWindow"),
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT, CW_USEDEFAULT,
            rect.right - rect.left, rect.bottom - rect.top,
            nullptr, nullptr, hInst, nullptr);
    if (hWindow_ == nullptr) {
        return false;
    }

    // ウインドウ表示
    ShowWindow(hWindow_, SW_SHOWNORMAL);
    UpdateWindow(hWindow_);

    return true;

}

bool Window::Terminate()
{
    DestroyWindow(GetWindowHandle());

    // ウインドウ クラスの登録解除
    UnregisterClass(wc_.lpszClassName, wc_.hInstance);
    hWindow_ = nullptr;
    return true;
}

bool Window::MessageLoop()
{
    // Windowsメッセージループ処理
    if (PeekMessage(&msg_, nullptr, 0, 0, PM_REMOVE)) { 
        TranslateMessage(&msg_); DispatchMessage(&msg_); 
    } 
    // アプリケーションが終わるときにmessageがWM_QUITになる 
    if (msg_.message == WM_QUIT) { 
        return false; 
    }
    return true;
}

/*static*/ LRESULT CALLBACK Window::MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
    case WM_DESTROY:
        // アプリ終了
        PostQuitMessage(0);
        return 0;
    }
    // デフォルト処理(必須)
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

結果表示

以上の、Application、Window、main処理を組み込めば以下のように、真っ白なWindowが表示、×ボタンで閉じることが可能となります。

show_window

次はDirectXを使った描画の準備を行っていきます。

次回の記事:【3Dゲーム開発 – 11】Direct3D11のセットアップ

3Dゲーム開発記事のまとめ:「3Dゲーム開発」

ソースコード

以下が今回の実装コード・リポジトリです。

https://github.com/prog-log/GameDev/blob/master/00-ShowWindow/

コメント

タイトルとURLをコピーしました