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

# 今回の目標
変数・条件分岐を応用し、四角形どうしの当たり判定と円形同士の当たり判定を作っていきましょう！
それに伴い今まで軽く説明してきた`GetColor`関数も同時に説明していきます。
円形どうしの当たり判定では`√(平方根)`がでてきますが、プログラミングでは式を立てるだけですので苦手な方でも安心してください(*'▽')

平方根やべき乗に関する関数の説明も交えていきますが変数・条件分岐を使え、合わせてキー入力を行えることが前提になりますのでわからない方は前回までの記事を見ていただけるとスムーズに進むと思います！  
[前々回の記事 : C++の勉強しながらDxLibで2Dゲーム作ってみない?(実践編)①](https://qiita.com/mayu___cs/items/4b6213e57579dea4d42b)
[前回の記事 　: C++の勉強しながらDxLibで2Dゲーム作ってみない?(実践編)②](https://qiita.com/mayu___cs/items/c8feeea825d37ade8eb2)
# この記事の対象
・Dxlibを使いゲーム開発をしてみたい方  
・C++を始めたばかりの初心者の方で作りながら覚えたい方  
・何かしらのライブラリやツールを使ってゲーム開発をしてみたい人方
# GetColor関数
今まで大した説明もなしに使ってきた`GetColor関数`ですが、この関数がなければ前回までの黒い画面に白い図形を描画することは不可能でした。
そんな`GetColor関数`ですが使い方を覚えてしまえば全く難しいものではありません。
###色について
色の表現は大きく分けて２つあり`加法混色`と`減法混色`というものに別れています。
そして基本普通にディスプレイを使っていればRGBの加法混色を使った色の表現をしています
この加法混色では`R(赤)`・`G(緑)`・`B(青)`の光の三原色を混ぜることで`0, 0, 0(黒)`から`255, 255, 255(白)`までの約1677万色を表現できます。

###関数の使い方
使い方は至ってシンプルで`GetColor()`の`()`内に3つの数字`(R, G, B)`を代入するだけです。
まずは前回のこのコードを見てみましょう

```cpp:maain.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 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;
}
```
しっかり使っていますね。
関数は四角形の描画関数内に引数として渡されています。
このコード内では関数の引数をすべて255(最大値)にしているので光の三原色を最大値混ぜて白にしています。
試しにこの部分をこのように書き換えてみてください。

```cpp
DrawBox(x, y, x + size, y + size, GetColor(255, 200, 200), true);
```
実行してみると四角形が薄いピンク色になります。
このように`GetColor`関数では色の値を指定してその色を描画関数などに引数で渡したりすることで色を付けることが可能になります。

# 当たり判定
四角形と円形の当たり判定は少し内容が異なるので分けて書いていきます。

##四角形同士の当たり判定
四角形同士の当たり判定は`GetColor関数`レベルで簡単です。
![graph.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390774/d653edf2-3c2e-c621-f0dc-c8680a707c3a.jpeg)
雑に作った四角形ですが目で見ればこの四角形は衝突しているとすぐわかります。
ただしこれはこのような条件が成り立ち衝突していると判定することができます。
①bx₁ < ax₂
②ax₁ < bx₂
③by₁ < ay₂
④ay₁ < by₂
そしてこれらはC++のif文を使ってこのまま書くことができます。
コードとしては以下のようになります。

```cpp
if ( bx1 < ax2 &&
     ax1 < bx2 &&
     by1 < ay2 &&
     ay1 < by2 ){
     //処理
}
```
ほんとにこのままですね。
`&&`は左辺と右辺がどちらも`true`の場合ですのでこのコードではすべての条件が`true`の場合に実行されます。
このコードとGetColorを応用し当たったら色が変わる処理を書いてみましょう。

```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; }

	const int speed = 5;
	const int size = 50;
	int player_x = 100;
	int player_y = 100;
	int enemy_x = (WIN_WIDTH / 2) - size;
	int enemy_y = (WIN_HEIGHT / 2) - size;

	char keys[256];

	while (true) {
		ClearDrawScreen();

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

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

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

		if (
			enemy_x  < player_x + size &&
			player_x < enemy_x + size &&
			enemy_y  < player_y + size &&
			player_y < enemy_y + size
			){
			//四角形(赤)描画
			DrawBox(player_x, player_y, player_x + size, player_y + size, GetColor(255, 0, 0), true);
		}
		else {
			//四角形(白)描画
			DrawBox(player_x, player_y, player_x + size, player_y + size, GetColor(255, 255, 255), true);
		}
		//オブジェクトの描画
		DrawBox(enemy_x, enemy_y, enemy_x + size, enemy_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;
}
```
このコードでは中心に配置されたオブジェクトに当たると自分の操作するオブジェクトの色が変わります。
実行するとこのようになります。
![Collision.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390774/ac6cb35e-ceb7-a466-382b-4cae3ad1232e.gif)

##円同士の当たり判定
###・sqrt関数
円の当たり判定では平方根が必須ですので平方根を扱う関数を紹介します。
通常数式では`√10`と書くと思いますがプログラムではそうは書けません。
具体的にこの平方根をC++で書くとこのようになります。

```cpp
sqrt(10);
```
見てわかるかと思いますが`√`記号を`sqrt`に置き換えて数字を`()`の中に入れるだけです。簡単ですね

###・pow関数
今回はこの関数を使うまでもないのですがそのうち役に立ちそうなので一応こちらも使っていこうと思います。
pow関数はC++でべき乗を求めるための関数です。
`10¹⁰`や`10^10`といったべき乗の計算はこのpow関数を使い求めることができます。
こちらも具体的に書いていきます。

```cpp
pow(10, 10);
```
これだけです。もっと説明するならば`xのy乗`を知りたいときは以下のようなコードを書きます。

```cpp
pow(x, y);
```
簡単ですね(*'▽')

###・当たり判定
ここまでやれば後は式を習得するだけです。
円の当たり判定では中心座標と半径から距離を求めて当たっているかを判定することができます。
複雑な説明はただの数学になってしまうので省略しますが円と円の中心間の距離と半径を使った当たり判定では以下のようなコードになります。

```cpp
int a = x2 - x1;
int b = y2 - y1;
int sum = sqrt(pow(a, 2) + pow(b, 2));

if(sum <= r1 + r2){
   //処理
}
```
おそらく上の三行は懐かしいと思ったり最近やった！と思う方がいるのではないでしょうか？
数式にするとこのようになります。
`√(x2 - x1)² + (y2 - y1)²`
まさにグラフで距離を導くために数学で使用したであろう有名な式です。
そして`if`の中では半径どうしを加算して求めた距離と比較をして当たっていた場合trueになり処理が実行されます。
ではこちらも実際にコードを書いていきましょう。
※***include\<cmath>はsqrtやpowなどの数学系関数を使うために書いています。***

```cpp:main.cpp
#include "DxLib.h"
#include <cmath>

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; }

	const int speed = 5;
	const int r = 50;
	int player_x = 100;
	int player_y = 100;
	int enemy_x = (WIN_WIDTH / 2) - r / 2;
	int enemy_y = (WIN_HEIGHT / 2) - r / 2;

	char keys[256];

	while (true) {
		ClearDrawScreen();

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

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

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


		int a = enemy_x - player_x;
		int b = enemy_y - player_y;
		int sum = sqrt(pow(a, 2) + pow(b, 2));
		if (sum <= r + r) {
			//円の描画(赤)
			DrawCircle(player_x, player_y, r, GetColor(255, 0, 0), true);
		}
		else {
			//円の描画(白)
			DrawCircle(player_x, player_y, r, GetColor(255, 255, 255), true);
		}
		//オブジェクトの描画
		DrawCircle(enemy_x, enemy_y, r, GetColor(255, 255, 255), true);

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

	DxLib_End();
	return 0;
}
```
このコードでは配置された丸型のオブジェクト同士が当たったら操作してるオブジェクトの色を変えています。
実行結果は以下のようになります。
![Collision2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390774/b7ab99e8-8c59-6c10-4d4d-3f8da388cb39.gif)

# 次回
さて、次回何しましょうか.......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)