DXライブラリによるゲーム作成【Windows版のゲーム作成準備編】

# ０．はじめに
DXライブラリというC++用ライブラリを使用し、ゲーム作成をしてみたので、
環境準備や実装内容などを整理し、備忘録として残しておく。
内容を書いていたら意外と長くなりそうだったので、以下の3編構成にする。

* **Windows版のゲーム作成準備編**
* [Windows版のゲーム作成（2Dスクロール）編](https://qiita.com/nekoshiki0904/items/0d5bc01b6d1ac6d29495)
* html版のゲーム作成（2Dスクロール）編

本記事は、上記の太字の内容について記載する。

**※免責事項(必ず、ご一読ください)**
本記事は、筆者以外の環境でも同様に成功することを保証するものではなく、
本記事により生じた、いかなる損害や損失についても、筆者は一切の責任を負いかねます。
古い情報が含まれていたり誤情報が含まれていたりする可能性もありますので、
必ずしも正確性を保証するものではありませんのでご了承ください。

# １．まえおき（読み飛ばしてOK）
* そもそも私の本業は組込み系。ゲーム作成やWeb系アプリの情報にうとかった。
* ゲーム自体は好きで、何か作ってみたいなーとは思ってた（漠然）。
* そんなとき、Youtubeにて、C++による[ゲーム作成](https://youtu.be/NvoWL7yCoiU)の動画を見つけた。
* 正直、ゲーム作成の敷居は昔と比べて下がった？と感じた（間違いなく先人の方々の努力のたまもの）。
* 自分にできることを調べ、ひとまず作ってみた。

# ２．動作確認環境
* Windows11
* VS2019（Microsoft Visual Studio Community 2019 16.11.17）
* ＤＸライブラリ Windows版 VisualStudio( C++ )用(Ver3.23b)

# ３．Microsoft Visual Studio Community 2019のインストール
* [Visual Studio インストーラ―](https://visualstudio.microsoft.com/ja/thank-you-downloading-visual-studio/?sku=Community&rel=16)をダウンロード
* 上記インストーラーに従い、インストール

# ４．DXライブラリのインストール
* [ＤＸライブラリのダウンロード](https://dxlib.xsrv.jp/dxdload.html)ページにて「ＤＸライブラリ Windows版 VisualStudio( C++ )用」をダウンロード
* ダウンロードしたzipを展開し、展開して作成された「DxLib_VC」を任意の場所に移動（私はCドライブ直下に配置）
　　<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740623/1ef1d9e2-c5d4-bf17-c157-1a2abba016cf.png">

# ５．VS2019プロジェクトの作成
* VS2019を起動
* 「新しいプロジェクトの作成」押下
* 「空のプロジェクト」押下後、「次へ」を押下
* 任意の「プロジェクト名」、「場所」、「ソリューション名」を記入し、「作成」を押下
　　<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740623/4e829794-934e-96b5-3ff5-bfb052f06576.png">

# ６．VS2019の設定
* 「ソリューションエクスプローラー」の「ソースファイル」を右クリックし、[追加][新しい項目]を選択し開く
　<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740623/1d9b6963-3bc5-4f1f-d515-ba8958d6854b.png">
　Main.cppを作成する
　<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740623/0b4e533c-0ac8-6b8d-ae92-66a9087efe30.png">

* 「ソリューションエクスプローラー」のプロジェクトを右クリックし、「プロパティ」を選択し開く
* [構成プロパティ][詳細]の文字セットを「マルチバイト文字セットを使用する」に変更
　<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740623/3812aa15-dd67-94af-242b-396f1a08c1b7.png">
* [構成プロパティ][C/C++][全般]の「追加のインクルードディレクトリ」に、DXライブラリのインクルードディレクトリを追加
※Cドライブ直下に展開した場合、C:\DxLib_VC\プロジェクトに追加すべきファイル_VC用
　<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740623/3fc034f1-2366-9fe6-a90a-8d4d58b792ff.png">
* [構成プロパティ][リンカー][全般]の「追加のライブラリディレクトリ」に、DXライブラリのライブラリディレクトリを追加
※Cドライブ直下に展開した場合、C:\DxLib_VC\プロジェクトに追加すべきファイル_VC用
　<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740623/bd822c0f-5ce4-7d31-c3b2-faa3c9c5cf80.png">

* [構成プロパティ][リンカー][システム]の「サブシステム」を、Windows(/SUBSYSTEM:WINDOWS)に変更
　<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740623/06a1952d-cb5d-c005-30c5-181334068af2.png">

ここまでの設定で、VS2019でDXライブラリをビルドするための設定が完了している。

# ７．ProjConf.h、Sub.h、Main.cppの実装と動作確認
ひとまず、60FPSで画面の反映をするプログラムを実装する。
実装する際に使用したDXライブラリの各関数の詳細については、
[関数リファレンスページ](https://dxlib.xsrv.jp/dxfunc.html)を参照した。

まずは設定用モジュールとして、ProjConf.hを作成する。
VS2019の「ソリューションエクスプローラー」の「ヘッダーファイル」を右クリックし、[追加][新しい項目]を選択し、追加する。
　<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740623/da6d7103-fcb5-32cf-2142-016bef83633d.png">
次にProjConf.hにシステム設定、ウィンドウ設定、キーボード設定、FPS設定用のdefineを実装。

```c:ProjConf.h
#pragma once

/*** システム設定 ***/
//#define DEF_SOUND_VALID			// サウンド有効

/*** ウィンドウ設定 ***/
#define WIN_MAX_X 660			// ウィンドウの横幅
#define WIN_MAX_Y 450			// ウィンドウの縦幅
#define WIN_POS_X 0				// ウィンドウの初期位置X
#define WIN_POS_Y 0				// ウィンドウの初期位置Y

/*** キーボード設定 ***/
#define DEF_KEY_PRESS_TIME 100	// キーボード長押し回数

/*** FPS設定 ***/
#define MicroSecond 1000000.0f	// 1マイクロ秒
#define MillSecond 1000.0f		// 1ミリ秒
#define WaitTimeMill 3000		// 最大で待てるミリ数
#define GameFPS 60				// 設定したいFPS
```

次に、フォントや色、入出力などの補助用モジュールとしてSub.hを作成する。Sub.hは、
* Fontクラス（1～30ptのフォントハンドル）
* Colorクラス（白、R、G、B、黒）
* Soundクラス（音出力（現在無効化））
* FPSクラス（FPS測定）
* Keyクラス（キーボード入力）

を実装。

```c++:Sub.h
#pragma once

/*** Fontクラス ***/
class
{
public:
	int FH[30 + 1];

	void Read()
	{
		for (int i = 0; i < 30 + 1; i++)
		{
			FH[i] = CreateFontToHandle("ＭＳ　ゴシック", i, 6, DX_FONTTYPE_NORMAL);
		}
	}
private:

}Fon;

/*** Colorクラス ***/
class
{
public:
	int Black;
	int Red;
	int Green;
	int Blue;
	int White;

	void Read()
	{
		White = GetColor(255, 255, 255);
		Red = GetColor(255, 0, 0);
		Green = GetColor(0, 255, 0);
		Blue = GetColor(0, 0, 255);
		Black = GetColor(0, 0, 0);
	}
private:

}Col;

#ifdef DEF_SOUND_VALID
/*** Soundクラス ***/
class
{
public:
	int BgmSound;

	void Read()
	{
		BgmSound = LoadSoundMem("./sound/XXX.wav");
	}
private:

}Snd;
#endif /* DEF_SOUND_VALID */

/*** FPSクラス ***/
class
{
public:
	LONGLONG FirsttakeTime = 0;		// 1フレーム目の計測時間
	LONGLONG NowtakeTime = 0;		// 現在の計測時間
	LONGLONG OldtakeTime = 0;		// 以前の計測時間

	float Deltatime = 0.000001f;	// デルタタイム（経過時間）
	int FrameCount = 1;				// 現在のフレーム数（1フレーム目からMAXフレーム目まで）
	float Average = 0.0f;			// 平均のFPS値

	void FPSInit() {
		FirsttakeTime = GetNowHiPerformanceCount();

		NowtakeTime = FirsttakeTime;
		OldtakeTime = FirsttakeTime;
		Deltatime = 0.000001f;
		FrameCount = 1;
		Average = 0.0f;

		return;
	}

	void FPSCheck() {
		NowtakeTime = GetNowHiPerformanceCount();
		Deltatime = (NowtakeTime - OldtakeTime) / MicroSecond;
		OldtakeTime = NowtakeTime;

		if (FrameCount == GameFPS)
		{
			LONGLONG TotalFrameTIme = NowtakeTime - FirsttakeTime;
			float CalcAverage = static_cast<float>(TotalFrameTIme) / GameFPS;
			Average = MicroSecond / CalcAverage;
			FirsttakeTime = GetNowHiPerformanceCount();
			FrameCount = 1;
		}
		else
		{
			FrameCount++;
		}
		return;
	}

	void FPSWait() {
		int wait = 0;
		wait = (MicroSecond / GameFPS * FrameCount) - (NowtakeTime - FirsttakeTime);	/* wait時間(micro sec) = 理想の時間 - 実際の時間 */
		wait /= MillSecond; /* wait時間(ms) */

		if (wait > 0 && wait <= WaitTimeMill) {
			WaitTimer(wait);
		}
		return;
	}
private:

}Fps;

/*** Keyクラス ***/
class
{
public:
	int input[256];		// キーボード入力情報

	int GetKey()
	{
		char allkey[256];
		GetHitKeyStateAll(allkey);
		for (int i = 0; i < 256; i++)
		{
			if (allkey[i] == 1) // 特定のキーは押されているか
			{
				if (input[i] < DEF_KEY_PRESS_TIME) // 長押し上限まで押されているかどうか
				{
					input[i] = input[i] + 1; // 保存
				}
			}
			else if (allkey[i] == 0) // 特定のキーは押されていないか
			{
				input[i] = 0;
			}
		}
		return 0;
	}
private:

}Key;
```

次に、メインモジュールとしてMain.cppを作成する。Main.cppは、
* メイン関数（WinMain）
    * ウィンドウの初期化
    * ループ処理（画面反映：設定したFPS値（GameFPS））

```c++:Main.cpp
/*** Header File ***/
#include "DxLib.h"
#include "ProjConf.h"
#include "Sub.h"

// プログラムは WinMain から始まります
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_  HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
	ChangeWindowMode(TRUE);							// ウィンドウモードで起動
	if (DxLib_Init() == -1)							// ＤＸライブラリ初期化処理
	{
		return -1;	// エラーが起きたら直ちに終了
	}

	/*** Window Init ***/
	SetWindowText("TEST 2D GAME");					// ウィンドウのタイトル
	SetWindowInitPosition(WIN_POS_X, WIN_POS_Y);	// ウィンドウの位置
	SetGraphMode(WIN_MAX_X, WIN_MAX_Y, 32);			// ウィンドウのサイズ
	SetBackgroundColor(255, 255, 255);				// ウィンドウの背景色
	SetDrawScreen(DX_SCREEN_BACK);					// 描画先画面を裏画面にする
	SetAlwaysRunFlag(TRUE);							// ウインドウ非アクティブ状態でも処理を続行する

	/*** FPS初期化 ***/
	Fps.FPSInit();

	/*** Read ***/
	Col.Read();
	Fon.Read();
#ifdef DEF_SOUND_VALID
	Snd.Read();

	/*** BGM開始 ***/
	PlaySoundMem(Snd.BgmSound, DX_PLAYTYPE_LOOP);
#endif /* DEF_SOUND_VALID */

	/*** ループ処理 ***/
	while (ScreenFlip() == 0 &&		// 裏画面の内容を表画面に反映
		ClearDrawScreen() == 0 &&	// 画面を初期化
		Key.GetKey() == 0 &&		// キーボード入力情報取得
		ProcessMessage() == 0)		// ウインドウのメッセージを処理
	{
		/* FPS計測開始 */
		Fps.FPSCheck();

		/* 本処理 */


		/* FPS表示 */
		DrawFormatStringFToHandle(10, 0, Col.Black, Fon.FH[10], "FPS:%4.1f", Fps.Average);

		/* FPSWait */
		Fps.FPSWait();
	}

	WaitKey();						// キー入力待ち

	DxLib_End();					// ＤＸライブラリ使用の終了処理

	return 0;						// ソフトの終了 
}

```

ビルド後、実行すると以下の画面が表示された。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740623/0ccbb942-943d-b39d-4359-9b1444d84595.png)
※モニタによっては60FPSの設定だと動作に違和感がある可能性がある。
　その場合は、垂直同期信号（WaitVSync(1);）を考慮した画面反映をする工夫が必要と思われる。
　（実際やってないので確証無し）


# ８．さいごに
あ、キーボード入力の動作確認してないや。
ループ処理の中で、Keyクラスの配列inputを確認すればいいかな。
Enterの値を確認する場合は、Key.input[KEY_INPUT_RETURN]。

サウンドのdefineを切ってるから現時点で音は流れないけど、
BGMを流したい場合は、音源ファイルを用意したうえで、
ProjConf.hのDEF_SOUND_VALIDを有効にして、
プロジェクトを作成したディレクトリにフォルダ「sound」を作成し、
音源ファイル「XXX.wav」をフォルダ「sound」に置くことで、BGMは流れる。

ひとまず、以下の3編構成のうち、準備編の内容がおわりました。
* **Windows版のゲーム作成準備編**
* [Windows版のゲーム作成（2Dスクロール）編](https://qiita.com/nekoshiki0904/items/0d5bc01b6d1ac6d29495)
* html版のゲーム作成（2Dスクロール）編

次の「[Windows版のゲーム作成（2Dスクロール）編](https://qiita.com/nekoshiki0904/items/0d5bc01b6d1ac6d29495)」では、
タイトル画面の作成や、キャラクター、ステージの移動や、アクション（ジャンプや当たり判定）などを記載~~予定です（いつ執筆しよう。。）~~。-->記載しました
「html版のゲーム作成（2Dスクロール）編」では、
ここまでに作成したC++ソースコードを、Emscriptenを使用してWebAssemblyにコンパイルし、
ウェブブラウザ上で実行するところまでを記載予定です（いつ執筆しよう。。）。

さいごに、
今までゲーム作成をしてこなかった私が少し調べれば作成できたのは、
先人の方々が残してきたものと、今現在も開発を進めている方々のおかげです。深く感謝申し上げます。