サイトアイコン GAMEWORKS LAB

初めてのVisual Studio ~ウィンドウの作成~

Visual Studioのプロジェクト作成

今回はVIsual Studioに慣れていない方のために、プロジェクトの作成からウィンドウの作成までを記事にしていきます。
※この記事は次の『初めてのDirectX9』の準備手順にもなっています。

Visual Studio 2015を使用していますが、最近のVisual Studioであれば他のバージョンでも基本的には同じです。
※日本語版の場合も配置は同じなので基本は下記の流れです。

1.Visual Studioを起動します。

2.[File] -> [New] -> [Porject]を選択します。

3.表示されるダイアログの[Visual C++] -> [Win32 Project]を選択します。
[Name]の項目は今回は次の題材の準備として『DirectX9』という名前にしますが、任意のプロジェクト名で問題ありません。
上記が完了したら[OK]を選択します。

4.[Next]を選択します。

5.[Empty project]にチェックを入れて[Finish]を選択します。

6.プロジェクトの作成が完了するとSolution Explorerに下記のように表示されます。

これでプロジェクトの作成は完了です。

プログラムの作成と実行

ここからは実際にプログラムを打って画面に絵を出すためのWindowの表示を行います。

先ずはプログラムを書くためのソースコードを追加します。

1.[Source Files]を右クリックして[Add] -> [New Item…]を選択します。

2.[Visual C++] -> [C++ File (.cpp)]を選択します。
[Name]の項目は任意ですがプログラムの起点となるコードは基本的に『main.cpp』とされています。
最後に[Add]を選択します。

3.無事に追加が出来た場合はプログラムを打ち込むエディタが表示されます。

これでプログラムを作成する準備が整いました。


では『main.cpp』にコードを打ち込んでみましょう。

// Win32APIの機能を使用するためにinclude(main.cppにwindows.hを含めるように)します。
#include <windows.h>

// エントリーポイント
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	return 0;
}


これがWin32のプロジェクトの基本となるプログラムです。
Win32のプロジェクトでは必ず上記のWinMainをエントリーポイント(起点)とするようになっています。

ここまで書くとプログラムをビルド(実行できる環境を作ること)が出来るようになります。

1.[Build] -> [Build Solution]を選択するとビルドが開始されます。
よく使う処理なのでF7キーをショートカットとして使うことが多いです。

2.ビルドが開始されると[Output]ウィンドウに結果が表示されていきます。
ビルドが終了すると「Build: * succeeded, * failed, ….」という表記が表示されます。
今回の場合は「Build: 1 succeeded, 0 failed, ….」となるはずです。

3.[Local Windows Debuger]を選択するとプログラムが実行されます。

今回は何もしないプログラムなので起動直後に終了すれば成功です。

ウィンドウの作成

次にウィンドウの作成をおこなっていきます。
ウィンドウは基本的にWin32APIの処理で作成します。

又、ウィンドウは拡張やスタイル(最大化、最小化、サイズ変更の有無)等の様々な設定が可能になっているので目的に応じて自由にカスタマイズすることができます。

今回は一般的な設定のWS_OVERLAPPEDWINDOWを使用します。

// Win32APIの機能を使用するためにinclude(main.cppにwindows.hを含めるように)します。
#include <windows.h>

// ウインドウプロシージャー
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
	switch( uiMsg )
	{
	case WM_CLOSE: // 閉じる際にWindowを破棄する
		DestroyWindow(hWnd);
		return 0;
	case WM_DESTROY: // プログラムの終了を通知する
		PostQuitMessage(0);
		return 0L;
	}
	// 既定のウィンドウプロシージャを呼び出す
	return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}

// エントリーポイント
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	// ウィンドウ位置・サイズの設定
	int x      = 0;
	int y      = 0;
	int width  = 640;
	int height = 480;
	// ウィンドウ名の設定
	const TCHAR* pWindowName = TEXT("DirectX9");

	WNDCLASSEX wcex;
	// ウインドウの設定
	wcex.cbSize		= sizeof(WNDCLASSEX);
	wcex.style		= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= GetModuleHandle(NULL);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= TEXT("DirectX9 Sample");
	wcex.hIcon		= LoadIcon(NULL, IDC_ARROW);
	wcex.hIconSm		= LoadIcon(NULL, IDI_APPLICATION);
	RegisterClassExW(&wcex);

	DWORD dwStyle = WS_OVERLAPPEDWINDOW;

	RECT Rect;
	Rect.left	= 0;
	Rect.top	= 0; 
	Rect.right	= width;
	Rect.bottom	= height;
	// ウィンドウのスタイルに合わせた適切なサイズを取得する
	AdjustWindowRect(&Rect, dwStyle, false);

	width = Rect.right - Rect.left;
	height = Rect.bottom - Rect.top;

	// ウインドウの生成
	HWND hWnd = CreateWindow(wcex.lpszClassName,
		pWindowName,
		dwStyle,
		x,
		y,
		width,
		height,
		NULL,
		NULL,
		GetModuleHandle(NULL),
		NULL);
	if( hWnd == NULL )
	{
		return false;
	}

	// ウインドウの表示
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);

	MSG msg;
	// メインループ
	while( true )
	{
		// メッセージが存在するか確認
		if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
		{
			// 終了通知が来ている場合は抜ける
			if( msg.message == WM_QUIT )
			{
				break;
			}
			// メッセージをウインドウプロシージャに転送
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return 0;
}


たったこれだけのプログラムでウィンドウの作成が出来てしまいます。
設定や決まりごとが多くて最初は難しいと感じると思いますが、何度も使う処理なので繰り返しているうちに分かるようになってくるはずです。

static LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)


ウィンドウプロシージャと書いている部分がありますが、これはウィンドウに対して通知されるメッセージを処理するための関数です。
通知されるメッセージとはキー入力や最小化、最大化、閉じる等の様々なものがあります。

最初から全てを理解する必要はないので、こうやってウィンドウが作成されているだなと雰囲気だけでも感じでもらえればいいかと思います。

上のプログラムの実行結果はこんな感じです。

まだウィンドウを作っただけなので真っ暗な画面のウィンドウが出来上がりました。

PC上のゲームもこうやって作ったウィンドウの上で動作するようになっています。
なのでここまでやった方はゲームプログラムの一端に触れたということですね。

初めての関数化

最後に作ったウィンドウのプログラムを関数化してみましょう。

先ずmain.cppを作った時と同じ手順でWindow.cppとWindow.hを作りましょう。

このようにファイルが2つ追加された状態にします。

ではまずヘッダーファイルから書いていきましょう。
ヘッダーファイルには他のプログラムから使用したい関数を宣言していきます。

Window.h
// 多重インクルード対策
#pragma once

#include <windows.h>

// シンプルなウィンドウの作成
bool CreateSimpleWindow(const TCHAR* pName, int x, int y, int width, int height);
// メッセージの更新
bool UpdateWindowMessage(void);
// 終了通知が来ているか?
bool IsQuitMessage(void);
// ウィンドウハンドルの取得
HWND GetWindowHandle(void);


次はcppファイルです。
cppファイルはmain.cppと同じようにプログラムの処理を記載していきます。

Window.cpp
// 作成したWindow.hを含める
#include "Window.h"

// ウィンドウハンドル
HWND g_hWnd = NULL;
// 終了通知が来ているか?
bool g_isQuitMessage = false;

// ウインドウプロシージャー
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
	switch( uiMsg )
	{
	case WM_CLOSE:		// 閉じる際にWindowを破棄する
		DestroyWindow(hWnd);
		return 0;
	case WM_DESTROY:	// プログラムの終了を通知する
		PostQuitMessage(0);
		return 0L;
	}
	// 既定のウィンドウプロシージャを呼び出す
	return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}

// シンプルなウィンドウの作成
bool CreateSimpleWindow(const TCHAR* pName, int x, int y, int width, int height)
{
	WNDCLASSEX wcex;
	// ウインドウの設定
	wcex.cbSize		= sizeof(WNDCLASSEX);
	wcex.style		= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= GetModuleHandle(NULL);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= TEXT("DirectX9 Sample");
	wcex.hIcon		= LoadIcon(NULL, IDC_ARROW);
	wcex.hIconSm		= LoadIcon(NULL, IDI_APPLICATION);
	RegisterClassExW(&wcex);

	DWORD dwStyle = WS_OVERLAPPEDWINDOW;

	RECT Rect;
	Rect.left	= 0;
	Rect.top	= 0; 
	Rect.right	= width;
	Rect.bottom	= height;
	// ウィンドウのスタイルに合わせた適切なサイズを取得する
	AdjustWindowRect(&Rect, dwStyle, false);

	width = Rect.right - Rect.left;
	height = Rect.bottom - Rect.top;

	// ウインドウの生成
	g_hWnd = CreateWindow(wcex.lpszClassName,
		pName,
		dwStyle,
		x,
		y,
		width,
		height,
		NULL,
		NULL,
		GetModuleHandle(NULL),
		NULL);
	if( g_hWnd == NULL )
	{
		return false;
	}

	// ウインドウの表示
	ShowWindow(g_hWnd, SW_SHOW);
	UpdateWindow(g_hWnd);

	return true;
}

// メッセージの更新
bool UpdateWindowMessage(void)
{
	MSG msg;
	// メッセージが存在するか確認
	if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
	{
		// 終了通知が来ている場合は抜ける
		if( msg.message == WM_QUIT )
		{
			g_isQuitMessage = true;
		}
		else
		{
			// メッセージをウインドウプロシージャに転送
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		return true;
	}
	return false;
}

// 終了通知が来ているか?
bool IsQuitMessage(void) { return g_isQuitMessage; }

// ウィンドウハンドルの取得
HWND GetWindowHandle(void) { return g_hWnd; }


これで先ほどmain.cppに書いたプログラムを関数化することが出来ました。

このように処理を関数化することでプログラムを用途ごとに切り分けて分かりやすくすることができます。
又、他の場所で使いまわしたい際に関数を呼び出すだけで使えるようになります。

では最後にmain.cppの中身を関数化したものに置き換えてみましょう。

main.cpp
#include "Window.h"

// エントリーポイント
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	// ウィンドウの生成
	if( !CreateSimpleWindow(TEXT("DirectX9"), 0, 0, 640, 480) )
	{
		return false;
	}

	// メインループ
	while( !IsQuitMessage() )
	{
		// メッセージの更新
		UpdateWindowMessage();
	}
	return 0;
}


関数化することでこんなにWinMain関数がスッキリしました!

コードも読みやすくなるので、プログラムを作る際はどういった単位で関数化するかを考えて作っていきましょう。

今回はスッキリしたところでこの辺りまでにします。

次回『始めてのDirectX9』へ続く。

モバイルバージョンを終了