C++の勉強しながらDxLibで2Dゲーム作ってみない?(実践編)②

# 今回の目標
変数を扱えるようになったところで今回はc++では条件分岐、DxLibではキー入力を使えるように頑張りましょう！
変数が使えることが前提の内容になるので前回の内容は習得しておくとスムーズに進むと思います(*'▽')  
[前回の記事 : C++の勉強しながらDxLibで2Dゲーム作ってみない?(実践編)①](https://qiita.com/mayu___cs/items/4b6213e57579dea4d42b)

# この記事の対象
・Dxlibを使いゲーム開発をしてみたい方  
・C++を始めたばかりの初心者の方で作りながら覚えたい方  
・何かしらのライブラリやツールを使ってゲーム開発をしてみたい人方

#前回のおさらい
前回は変数と描画関数について触れました。
その中で`int型`(整数型)というのが出てきましたがこれは宣言して代入や四則演算ができましたね？
またインクリメントやでクリメントなんて使い方もありました。
具体的に変数はこのように扱います。  
※前回も当たり前のように登場していた`//`や`/**/`ですがこれはコメントアウトといいプログラムにコメントを追加する機能です。(説明不足)

```cpp
//int型のxという変数を宣言し0で初期化(代入)
int x = 0;

//int型のyという変数を宣言し10で初期化(代入)
int y = 10;

//int型変数xにint型変数yを加算
x += y;

//int型変数xをインクリメント(+1)
x++;
```
今回はこれらの変数と条件分岐を使い"スクリーンセーバーのあれ"を作りましょう。
おそらく作ってたら気づきます。

# 条件分岐(if-else文)
C++では`if`という条件分岐が存在します。
このifは基本的にこのような使い方になります。

```cpp
if(/*条件式*/){
   //処理
}
```
`if`の`( )`内に書かれた条件式が`true(真)`の場合`{ }`内の処理がされます。
例えば変数`x`が100より大きくなった場合の`if`文はこのように書きます。

```cpp
if(x > 100){
   //処理
}
```
このコードでは`x`が`100`より大きくならないと実行されませんが、そうじゃない場合の処理も書く場合はこのようになります。

```cpp
if(x > 100){
   //処理
}
else{
   //処理
}
```
この場合は`if`文で条件が`true`にならなかった場合`else`内の処理が実行されます。
また、この`if`文での条件式に使われる記号は演算子といいます。
C++ではいくつかの演算子が存在しますが変数より数が少なく説明しやすいので今回は記事中で説明してみます。
以下の表では式が成立した場合`true(真)`を、そうでなかった場合は`false(偽)`を返します

| 演算子 | 式 | 意味 |
|:-:|:-:|:-:|
|  <   | x < y   | xがyより小さい場合 |
|  >   | x > y   | xがyより大きい場合 |
|  <=  | x <= y  | xが以下の場合 |
|  >=  | x >= y  | xがy以上の場合 |
| ==   |  x == y | xとyが等しい場合 |
|  !=  | x != y  | xよyが等しくない場合 |


ここまで使えるようになると"スクリーンセーバーのあれ"が作れるようになります。
まずはこのようなコードを書いてみてください。
`else if`がでてきますが　後程説明します。

```cpp:main.cpp
#include "DxLib.h"

const char *TITLE = "Untitled";
const int WIN_WIDTH = 960;
const int WIN_HEIGHT = 540;

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
	ChangeWindowMode(true);
	SetWindowSizeChangeEnableFlag(false, false);
	SetMainWindowText(TITLE);
	SetGraphMode(WIN_WIDTH, WIN_HEIGHT, 32);
	SetWindowSizeExtendRate(1.0);
	SetBackgroundColor(35, 35, 35);
	SetDrawScreen(DX_SCREEN_BACK);

	if (DxLib_Init() == -1) { return -1; }

	int speed_x = 5;
	int speed_y = 5;
	int x = 100;
	int y = 100;
	int size = 50;

	while (true) {
		ClearDrawScreen();

		if (x <= 0) {
			speed_x = -speed_x;
		}
		else if (x >= WIN_WIDTH - size) {
			speed_x = -speed_x;
		}
		if (y <= 0) {
			speed_y = -speed_y;
		}
		else if (y >= WIN_HEIGHT - size) {
			speed_y = -speed_y;
		}

		x += speed_x;
		y += speed_y;

		DrawBox(x, y, x + size, y + size, GetColor(255, 255, 255), true);

		ScreenFlip();
		WaitTimer(20);
		if (ProcessMessage() == -1) { break; }
		if (CheckHitKey(KEY_INPUT_ESCAPE) == 1) { break; }
	}

	DxLib_End();
	return 0;
}
```
今回はx方向に5、y方向に5を加算し、壁に当たったら跳ね返るという処理を書きました。
x,yどちらも処理がたいして変わらないためxだけ説明していきます。
x方向の条件式ではこのように書かれています。

```cpp
if (x <= 0) {
   speed_x = -speed_x;
}
else if (x >= WIN_WIDTH - size) {
   speed_x = -speed_x;
}
```
この部分では`speed_x`という変数を`x`が壁に当たったら反転する処理をしています。
最初のifでは`x`が`0`以下になった場合(左側の壁に当たった場合)変数`speed_x`をの値を反転します。
こうすることで壁に当たった場合壁とは反対側に四角形を移動するようにしています。

次の`else if`というのは上の`if`がfalse(偽)であり、なおかつ`else if()`の`()`内の条件がtrue(真)の場合に実行されます。
`else`にただ`if`がくっついただけです。
そしてこの`else if`では右側の壁に当たった場合の処理をしています。
`WIN_WIDTH`が画面サイズなので画面サイズから四角形の一辺のサイズを引くことで左上をもとに描画しているため右の壁に四角形の右側の辺が当たった場合変数`speed_x`を反転し反対側に四角形を移動するようにしています。
そしてこのコードを実行するとこのような動きになります。
![ScreenSave.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390774/920ad22f-8fa9-e8e2-9969-b6e55d82d6ba.gif)
これ、見たことありませんか？
"スクリーンセーバーのあれ"です。
条件分岐を使えるとこんなことができるようになります。
それでは次のキー入力でさらに応用していきましょう。

# キー入力
おそらく多くのゲームではキーボードやマウス、それにコントローラーなどキー入力をしてプレイヤーを操作しますよね。
ゲームにおいてキー入力は必須といっても過言ではないかと思います(一部そうではないゲームはありますが...)
ですのでこのキー入力と条件分岐を応用してプレイヤーの操作をしていきましょう。
### ・CheckHitKey関数
`CheckHitKey`はDxLibで用意されている関数でこれを使うことによりキー入力を知ることができます。
そして便利なことにDxLibではキーがマクロ登録されているのでスペースキーが押されているか知りたい場合は`CheckHitKey`に`KEY_INPUT_SPACE`というマクロを引数で渡してあげるだけでわかります。
先ほどのコードを一部変えてこのようなコードを書いてみてください。

```cpp:main.cpp
#include "DxLib.h"

const char *TITLE = "Untitled";
const int WIN_WIDTH = 960;
const int WIN_HEIGHT = 540;

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
	ChangeWindowMode(true);
	SetWindowSizeChangeEnableFlag(false, false);
	SetMainWindowText(TITLE);
	SetGraphMode(WIN_WIDTH, WIN_HEIGHT, 32);
	SetWindowSizeExtendRate(1.0);
	SetBackgroundColor(35, 35, 35);
	SetDrawScreen(DX_SCREEN_BACK);

	if (DxLib_Init() == -1) { return -1; }

	int speed_x = 5;
	int speed_y = 5;
	int x = 100;
	int y = 100;
	int size = 50;

	while (true) {
		ClearDrawScreen();

		if (x <= 0) {
			speed_x = -speed_x;
		}
		else if (x >= WIN_WIDTH - size) {
			speed_x = -speed_x;
		}
		if (y <= 0) {
			speed_y = -speed_y;
		}
		else if (y >= WIN_HEIGHT - size) {
			speed_y = -speed_y;
		}

		if (CheckHitKey(KEY_INPUT_SPACE)) {
			x += speed_x;
			y += speed_y;
		}

		DrawBox(x, y, x + size, y + size, GetColor(255, 255, 255), true);

		ScreenFlip();
		WaitTimer(20);
		if (ProcessMessage() == -1) { break; }
		if (CheckHitKey(KEY_INPUT_ESCAPE) == 1) { break; }
	}

	DxLib_End();
	return 0;
}
```
このコードでは以下の部分でスペースが押されると座標xやyに変数`speed_x`や変数`speed_y`を加算し、動かす処理をしています。

```cpp
if (CheckHitKey(KEY_INPUT_SPACE)) {
   x += speed_x;
   y += speed_y;
}
```

ですがいちいち`CheckHitKey`を呼び出しのも"なんだかなぁ..."と思うのです。
ですのですべてのキー情報を格納する変数を作りすべてのキー情報を教えてくれる関数で格納していきましょう。
### ・GetHitKeyStateAll関数
`GetHitKeyStateAll`関数を使えばすべてのキー情報が分かります。
この関数に引数をとして`char型`の配列を渡すことでキー情報を格納できるのですが、この仕組みを説明すると`ポインタ`や`アドレス`、配列の仕組みやアドレスと配列の関係性を説明しなけれならないのでもう少し先に進んだら説明します。
まずはこうすることによって格納できるという事だけわかっていただければ十分です。
では以下のコードを書いてみてください。

```cpp:main.cpp
#include "DxLib.h"

extern const char *TITLE = "Untitled";
extern const int WIN_WIDTH = 960;
extern const int WIN_HEIGHT = 540;

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
	ChangeWindowMode(true);
	SetWindowSizeChangeEnableFlag(false, false);
	SetMainWindowText(TITLE);
	SetGraphMode(WIN_WIDTH, WIN_HEIGHT, 32);
	SetWindowSizeExtendRate(1.0);
	SetBackgroundColor(35, 35, 35);
	SetDrawScreen(DX_SCREEN_BACK);

	if (DxLib_Init() == -1) { return -1; }

	int x = 100;
	int y = 100;
	const int speed = 5;
	const int size = 50;

	char keys[256];

	while (true) {
		ClearDrawScreen();

		//すべてのキー情報を格納
		GetHitKeyStateAll(keys);

		//キー操作
		if (keys[KEY_INPUT_A]) {
			x -= speed;
		}
		else if (keys[KEY_INPUT_D]) {
			x += speed;
		}

		if (keys[KEY_INPUT_W]) {
			y -= speed;
		}
		else if (keys[KEY_INPUT_S]) {
			y += speed;
		}

		//四角形描画
		DrawBox(x, y, x + size, y + size, GetColor(255, 255, 255), true);

		ScreenFlip();
		WaitTimer(20);
		if (ProcessMessage() == -1) { break; }
		if (CheckHitKey(KEY_INPUT_ESCAPE) == 1) { break; }
	}

	DxLib_End();
	return 0;
}
```
んふwそれっぽくなってきましたね！(キモい)
早速説明です。
`keys[256]`という`char型`の変数がありますが今はおまじないです。
簡単に説明すると256個のchar型の変数がこれです。
そして`GetHitKeyStateAll(keys)`で`keys`にキーの情報を格納しています。
こうすることによって後のコードがすっきりしますしいちいち`CheckHitKey`関数を呼ばずに済むようになります。
そしてこの変数たちを使いキー入力でキャラクターを操作している部分がこちらです。

```cpp
if (keys[KEY_INPUT_A]) {
   x -= speed;
}
else if (keys[KEY_INPUT_D]) {
   x += speed;
}

if (keys[KEY_INPUT_W]) {
   y -= speed;
}
else if (keys[KEY_INPUT_S]) {
   y += speed;
}
```
一目瞭然のコードなので説明はいりませんねw
まぁ簡単に説明すると

| 押されているキー | 処理 |
|:-:|:-:|
| W | y方向にspeed分減らし上に移動 |
| A | x方向にspeed分減らし左に移動 |
| S | y方向にspeed分増やし下に移動 |
| D | x方向にspeed分増やし右に移動 |
これだけです。
これだけでキャラクターの操作ができます。
いらないとは思いますがGIF置いときます()
![Operation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390774/45413625-ed29-7be4-4793-9ee1e23bfe65.gif)
このようにWASDで操作することによって自分が動かしたい方向にオブジェクトを動かすことができました。
これをキャラクターに置き換えるだけでキャラクター操作ができるようになります(実際はこれだけではないですがw)

# 次回
とうとう条件分岐とキー入力ができるようになりましたね！
ここまで来たら次回は一度変数に戻り配列をやるか当たり判定をやるか悩むところですが次回までに考えておきますw
当たり判定の場合は数学的要素が加わるので`√(平方根)`アレルギーの方は覚悟しておいてくださいw
まぁ自分で計算するわけではないので身構えなくても大丈夫ですがw...
次回まで少々お待ちください！

# あとがき
正直プログラミングって変数と条件分岐を習得すると一定以上のものが作れるようになるので応用していろいろ作ってみたり動作を確認してみるといいかもしれません(*'▽')
これから難しくなってきますがね...ﾌ ﾌ ﾌ ﾌ ﾌ ﾌ
(ﾌ ﾌ ﾌ ﾌ ﾌ ﾌがわかるプログラマーはこの記事読んでないでデバッグやら頑張ってください)

###作者について
詳しくはわんころメソッド();HPにどうぞ(*'▽')

・[わんころメソッド();HP](http://mayu-cs.xyz/)
・[Twitter(まゆC#)](https://twitter.com/mayu___cs)
・[GitHub(まゆC#)](https://github.com/mayu-cs)

コミュニティ
Discordで雑談から質問まで様々なチャンネルがあるまゆC#のコミュニティサーバーがあります！
わからないことがあればこちらでもお答えするのでぜひ！

[Discordサーバー : わんころメソッド(雑談);](https://discord.com/invite/TZVTBkk)