PaperSloth’s diary

主にゲーム開発関連についての記事を書きます。

Poem Window作る

環境

Visual Studio 2015 Community
C/C++(C++11)
SVN


概要

ポエムです。制作日記みたいなものです。

前回はコピーを禁止する基底クラスつくりました。
似たようなのが調べたらそこかしこに落ちてます。
papersloth.hatenablog.com


Windowを作る

やってることはWinUser.hのCreateWindowをして
同じくWinUser.hのShowWindowしてるだけ。

とりあえずmain関数をぺたり。

// Copyright(c) 2018 PaperSloth

#if _DEBUG
#include <crtdbg.h>
#endif

#include "PSFramework/Windows/Window.hpp"
#include <memory>

using namespace PS;

int main()
{
#if _DEBUG
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

    std::unique_ptr<Window> window = std::make_unique<Window>();
    window->Initialize();

    return window->MainLoop();
}

デバッグビルドの時はメモリリークのチェックを行うようにしました。
あとはWindowクラスを作ったので、それを生成して初期化してループ回しておしまい。
何にも難しいことはない。



続いてWindowクラスをぺたり。
Window.h

// Copyright(c) 2018 PaperSloth

#pragma once

#include "PSFramework/Core/NonCopyable.hpp"
#include <Windows.h>

namespace PS
{
    class Window : public NonCopyable
    {
    public:
        Window();
        virtual ~Window();

        HRESULT	Initialize(LPCSTR title = "PSFramework",
            const UINT width = 640,
            const UINT height= 480);
        // TODO: Windowクラスから除外する
        int MainLoop();

    private:
        HRESULT Create(const UINT width, const UINT height);
        HRESULT Register(LPCSTR title);

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

    private:
        HWND	   hWnd;
        HINSTANCE  hInstance;
        WNDCLASSEX wcex;
    };
}

初期化関数でWindow名とサイズを渡します。
今回は特に変更がないので、デフォルト引数のまま使用しています。

Create関数でWindowCreateして
Register関数でRegisterClassするだけ。
TODOにも書きましたが、メインループはWindowじゃないよね。ってことでそのうち省く。



続いて、長いのですがWindow.cppもぺたり。
Window.cpp

// Copyright(c) 2018 PaperSloth

#include "Window.hpp"

using namespace PS;

Window::Window()
    : hWnd(nullptr),
      hInstance(nullptr)
{
    SecureZeroMemory(&wcex, sizeof(wcex));
}

Window::~Window()
{
    DestroyWindow(hWnd);
    UnregisterClass(wcex.lpszClassName, wcex.hInstance);
}

HRESULT Window::Initialize(LPCSTR title, const UINT width, const UINT height)
{
    HRESULT hr = Register(title);
    if (FAILED(hr))
    {
        MessageBox(nullptr, "Failed Register Window", "ERROR", MB_OK);
        return hr;
    }

    hr = Create(width, height);
    if (FAILED(hr))
    {
        MessageBox(nullptr, "Failed Create Window", "ERROR", MB_OK);
        return hr;
    }

    ShowWindow(hWnd, SW_SHOWNORMAL);
    UpdateWindow(hWnd);

    return hr;
}

int Window::MainLoop()
{
    MSG msg;
    SecureZeroMemory(&msg, sizeof(msg));

    while (msg.message != WM_QUIT)
    {
        if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            // TODO: メイン処理
        }
    }

    return static_cast<int>(msg.wParam);
}

HRESULT Window::Create(const UINT width, const UINT height)
{
    RECT rect = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) };
    const DWORD WindowStyle = WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME;
    AdjustWindowRect(&rect, WindowStyle, FALSE);

    this->hWnd = CreateWindow(
        wcex.lpszClassName,
        wcex.lpszClassName,
        WindowStyle,
        (GetSystemMetrics(SM_CXSCREEN) - width)  / 2,
        (GetSystemMetrics(SM_CYSCREEN) - height) / 2,
        width,
        height,
        nullptr,
        nullptr,
        wcex.hInstance,
        this
    );

    if (!hWnd)
    {
        MessageBox(nullptr, "Failed create window", "ERROR", MB_ABORTRETRYIGNORE);
        return E_FAIL;
    }

    return S_OK;
}

HRESULT Window::Register(LPCSTR title)
{
    const HINSTANCE hInst = GetModuleHandle(nullptr);

    wcex.cbSize		= sizeof(wcex);
    wcex.style		= CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc	= WindowProc;
    wcex.cbClsExtra		= 0;
    wcex.cbWndExtra		= 0;
    wcex.hInstance		= hInst;
    wcex.hIcon		= LoadIcon(nullptr, IDI_APPLICATION);
    wcex.hCursor		= LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground	= reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
    wcex.lpszMenuName	= nullptr;
    wcex.lpszClassName	= title;

    if (!RegisterClassEx(&wcex))
    {
        return E_FAIL;
    }

    this->hInstance = hInst;

    return S_OK;
}

LRESULT Window::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_KEYDOWN:
            switch (wParam)
            {
                // TODO: キー入力処理は別途メインループから行う
                case VK_ESCAPE:
                    SendMessage(hWnd, WM_CLOSE, 0, 0);;
                    break;
            }
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, msg, wParam, lParam);
    }

    return 0;
}

デストラクタでDestroyWindowとUnregisterClassしてるけど、これってなくても問題なさそう・・・
そのうち調べて不要なら消す。

コールバック関数で一旦はEsc押下時に終了させるようにしているけど
これはinput系の機能作った時にそっちに移行する予定です。

動かしたら真っ白なWindowが出る。だけ。
f:id:PaperSloth:20180227232624p:plain

次回くらいから描画APIほりこむ。かも。