C++ OpenGL 3D GameTutorial 1: win32 APIでウィンドウを作る 学習メモ


動画アドレスicon-default.png?t=N7T8https://www.youtube.com/watch?v=jHcz22MDPeE&list=PLv8DnRaQOs5-MR-zbP1QUdq5FL0FWqVzg

 1. エントリー機能

         まず、エントリ関数のメイン コードを見てください。

#include<OGL3D/Game/OGame.h>

int main()
{
	OGame game;
	game.Run();

	return 0;
}

        ここに C++ 構文に関する質問があります。ここに OGame game があります。このステートメント行は実際に OGame オブジェクトを構築しており、その名前は game です。C# のように new キーワードを使用したい場合は、次のようにする必要があります。

OGame* game = new OGame();
game->Run();

        このように理解すべきでしょうか? new キーワードは実際には新しいメモリを開くことを意味します。返されるのはこのメモリのポインタであるため、値は実際には OGame クラスへのポインタである OGame* に割り当てられます。

        C# の New も同じ意味を持つはずですが、その書き方により、開発者はそれがポインタではなくオブジェクトに割り当てられていると考えてしまいます。

 2.Oゲームカテゴリー

        最初にコードを貼り付けます。

        OGame.h

#pragma once
#include<memory>

class OWindow;
class OGame
{
public:
	OGame();
	~OGame();

	void Run();
	void Quit();
protected:
	bool m_isRunning = true;
	std::unique_ptr<OWindow> m_display;
};

         OGame.cpp

#include<OGL3D/Game/OGame.h>
#include<OGL3D/Window/OWindow.h>
#include<Windows.h>

OGame::OGame()
{
	m_display = std::unique_ptr<OWindow>(new OWindow());
}

OGame::~OGame()
{
}

void OGame::Run()
{
	MSG msg;
	while (m_isRunning && !m_display->isClosed())
	{
		if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		Sleep(1);
	}
}

void OGame::Quit()
{
	m_isRunning = false;
}

        まず std::unique_ptr スマート ポインターを見てみましょう。

std::unique_ptr<OWindow> m_display;

        このコード行は、OWindow タイプのオブジェクトを指す m_display という名前のスマート ポインターを定義します。

#include<memory>;

        これは記憶を賢く解放してくれるそうですが、ははは、まずはこのように理解して、今後の効果を見てみましょう。

        PeekMessage に似た GetMessage 関数もありますが、この 2 つの違いは、メッセージが返されない場合、PeekMessage は null 値を返すのに対し、GetMessage はプログラムをスリープ状態にすることです。また、GetMessage はメッセージ キューからメッセージを取得した後、確実にメッセージを削除しますが、PeekMessage は最後のパラメーターを通じてメッセージを削除するかどうかを決定できます。GetMessage はメッセージを「奪う」ものとして理解され、PeekMessage はメッセージを「覗く」ものとして理解されますが、必ずしも「奪う」わけではありません。ここでは PeekMessage の最後のパラメータを PM_REMOVE に設定します。これは、「奪う」という状況です。

        TranslateMessage 関数は、キーボード メッセージを文字メッセージに変換し、変換された新しいメッセージを呼び出しスレッドのメッセージ キューに再配信するために使用されます。

        たとえば、キーボードの文字キーをタップすると、システムは WM_KEYDOWN および WM_KEYUP メッセージを生成します。これら 2 つのメッセージ (wParam と lParam) の追加パラメーターには、仮想キー コードやスキャン コードなどの情報が含まれており、多くの場合、プログラム内の特定の文字の ASCII コードを取得する必要があります。TranslateMessage 関数は、WM_KEYDOWN と WM_KEYUP を変換できます。メッセージ 組み合わせは WM_CHAR メッセージ (メッセージの wParam 追加パラメータには文字の ASCII コードが含まれます) に変換され、変換された新しいメッセージが呼び出しスレッドのメッセージ キューに配信されます。TranslateMessage 関数は元のメッセージを変更しないことに注意してください。

        DispatchMessage はオンラインで説明するのが少し複雑で、わかりにくく見えます。私の理解では、この関数は window オブジェクトの lpfnWndProc 属性に対応する関数にメッセージを送信します。具体的な分散メカニズムはゆっくり検討しますが、実装されているのは 1 つだけです。私たちのプログラムのウィンドウです、間違って送信してもチャンスはありません、何も言うことはありません。

3.OWindowクラス

        最初にコードを貼り付けます。

        OWindow.h

#include<Windows.h>
class OWindow
{
public:
		OWindow();
		~OWindow();

		void OnDestroy();
		bool isClosed();
private:
	HWND m_handle = nullptr;
};

        OWindow.cpp

#include<OGL3D/Window/OWindow.h>
#include <Windows.h>
#include<assert.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
	case WM_DESTROY:
	{
		OWindow* window = (OWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
		window->OnDestroy();
		break;
	}
	default:
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}
	return NULL;
}

OWindow::OWindow()
{
	WNDCLASSEX wc = {};
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.lpszClassName = L"OGL3DWindow";
	wc.lpfnWndProc = WndProc;

	assert(RegisterClassEx(&wc));

	RECT rc = { 0,0,1024,768 };
	AdjustWindowRect(&rc, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, false);

	m_handle = CreateWindowEx(NULL, L"OGL3DWindow", L"Parcode | OpenGL 3D Game", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT,
		rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, NULL, NULL);

	assert(m_handle);

	SetWindowLongPtr(m_handle, GWLP_USERDATA, (LONG_PTR)this);

	ShowWindow(m_handle, SW_SHOW);
	UpdateWindow(m_handle);
}

OWindow::~OWindow()
{
	DestroyWindow(m_handle);
}

void OWindow::OnDestroy()
{
	m_handle = nullptr;
}

bool OWindow::isClosed()
{
	return !m_handle;
}

        なぜ文字列の前に L を追加するのでしょうか? 各文字が 2 バイトを占めるように Unicode 文字列に変換された ANSI 文字列を表します。

        RegisterClassEx はウィンドウの登録に使用されるようで、おそらくトラバースと検索を容易にするためにリストにウィンドウ ポインタを追加します。RegisterClassEx は、コンパイル環境に応じて RegisterClassExA または RegisterClassExW に置き換えられます。RegisterClassExA を使用してウィンドウ クラスを登録する場合、アプリケーションは、登録されたクラスのウィンドウによって返されるメッセージが ANSI 文字セットのテキストおよび文字パラメータを使用していることをシステムに通知します。RegisterClassExW を使用してウィンドウ クラスを登録する場合、アプリケーションはシステムに通知します。システムはメッセージのテキスト パラメータを Unicode で渡す必要があります。つまり、現在の環境では、RegisterClassEx は RegisterClassExW と同等である必要があり、メッセージ文字列が Unicode 文字列である必要があるため、前述のメッセージ文字列の前に文字 L を追加する必要があることがわかります。

        SetWindowLongPtr 関数は、ウィンドウの拡張スタイルまたは追加データを設定するために使用されます。この機能は、32 ビット版と 64 ビット版の Windows システムと互換性があります。ここでは、OWindow オブジェクトへのポインタが LONG_PRT 型に変換され、ウィンドウ データ オフセット GWLP_USERDATA に保存されます。うーん、よくわかりませんが、要するに OWindow オブジェクトへのポインタを保存しておき、必要なときに取り出して OWindow オブジェクトにアクセスできるようにするということです。WinProc メソッドから、SetWindowLongPtr 関数に対応する GetWindowLongPtr 関数が値を取得してメソッドを呼び出す方法がわかります。

OWindow* window = (OWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
window->OnDestroy();

おすすめ

転載: blog.csdn.net/ttod/article/details/135382120