【ゲームで学ぶオブジェクト思考】状態をどう作る？

# **はじめに**

## 免責事項
記事中のコードはc++を基本として書いていますが
あくまで考え方を示すサンプル的な立ち位置です。
動作確認はしていないので、コードがおかしいところは脳内補間していただければと思います。

## 前に書いた記事
[【ゲームで学ぶオブジェクト思考】HP(ヒットポイント)をどう作る？](https://qiita.com/Nekonecode/items/d194c66ddb8a27dc4345)
よければこちらもご覧ください。

# <font color="#009F8C">**状態とは**</font>

![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/57524/587d886a-2764-cd66-e76f-25fceba85c2b.png)

こんな感じのやつについてです。
毒ってると体力が徐々に減ったり、眠ってると行動とれなくなったり
というのを判断するために状態について考えていこう。


# <font color="#009F8C">**シンプルに作ってみる**</font>

ゲーム作りはじめのころはこんな感じで状態を作っていました。

## 各種状態のフラグを用意

```cpp
bool isPalsy  = false; // 麻痺
bool isPoison = false; // 毒
bool isSleep  = false; // 睡眠
bool isAnger  = false; // 怒り
bool isSad    = false; // 悲しみ
```

## 麻痺ってる処理はこう

```cpp
// 麻痺ったら、麻痺フラグをtrue
isPalsy = true;

// 麻痺ってるね？
if(isPalsy){
  // Yes I do!
}
```

## 毒ってる処理はこう

```cpp
// 毒ったら、毒フラグをtrue
isPoison = true;

// 毒ってるね？
if(isPosion){
  // Yes I do!
}
```

いいかんじ、いけてるいけてる。

## 他にもありそうな処理を考えてみよう。

```cpp
// 状態異常にかかってないよね？を調べるならこう
if(!isPalsy && !isPoison && !isSleep && !isAnger && !isSad) {
  // I am good!
}

// 万能薬を使った時は状態異常を全部直すぞ
isPalsy = isPoison = isSleep = isAnger = isSad = false;
```

やばい、これ新しい状態が増える度に修正入るパティーン

# <font color="#009F8C">**状態毎にいちいちフラグ作りたくないぞ**</font>

このままだと

- <font color="red">沈黙</font>とか新しい状態増えるとフラグ新しく作らないといけない。
- 万能薬の処理とか判定も書き直さないといけない。
- バグらせる自信がつく。

こんな時はあれだな、あれ、<font color="red">**BITフラグ**</font>。

# <font color="#009F8C">**BITフラグとは？**</font>

これはオブジェクト思考ではなくて
プログラムのテクニック的な話ですが簡単に説明をしておきます。

```cpp
char state = 0;
```

例えばこのstateですが、パソコンの内部ではどうなってるでしょうか？
はい、こうなってます。

![キャプチャ2.PNG](https://qiita-image-store.s3.amazonaws.com/0/57524/29e26e2a-5dea-cdab-1a7d-66cb498cdc61.png)

パソコンは内部ではデータを0と1だけで表現しています。
つまるところ、char型の変数というのは0か1を入れられる箱が8個あるのです。
これって<font color="red">**bool型のフラグが8個あるのと同じですよね**</font>っていう。

![キャプチャ3.PNG](https://qiita-image-store.s3.amazonaws.com/0/57524/ea7ba449-ca07-f600-8895-5e150dbfcb43.png)

この1bitをフラグとして使おうっていうのが<font color="red">**BITフラグ**</font>です。
さて、という事でBITを扱う事を前提に<font color="red">**状態**</font>を扱いやすいモノにしていこう。

# <font color="#009F8C">**状態をクラスにしよう**</font>
まずは状態(State)クラスを作るよ。
<font color="red">※なおbitの操作方法については本筋から外れるので割愛します。</font>

```cpp
class State 
{
private:
	char mFlag; // 状態を管理するフラグ(8bitなので8種類まで管理できる)

public:
	// コンストラクタではFlagを0に初期化(不定値対策)
	State() :mFlag(0) {}

	// ここから色んなメソッドを作っていくよ。
};
```

## **状態の種類を定義しよう。**

```cpp
// 状態の種類(列挙型使うとわかりやすいね)
struct Kind {
	enum {
		Palsy  = 1 << 0, // 00000001 麻痺
		Poison = 1 << 1, // 00000010 毒
		Sleep  = 1 << 2, // 00000100 睡眠
		Anger  = 1 << 3, // 00001000 怒り
		Sad    = 1 << 4, // 00010000 悲しみ
		All    = 0xFF,   // 11111111 全て
	};
};
```

状態の種類を列挙型で定義してみました。
それぞれ、bitを1桁ずつずらして1になるように。

## **状態を変更するメソッドを作っていこう。**

```cpp
// 状態をリセット(健康にする)
void reset() {
	mFlag = 0; // 0にするだけ
}

// 指定された状態をON(有効)にする
void on(char flag) 
{
	mFlag |= flag; // 論理和をとればいいね。
}

// 指定された状態をOFF(無効)にする
void off(char flag) 
{
	mFlag &= ~flag; // BIT反転(ﾁﾙﾀﾞ記号)させて論理積とればいいね。
}
```

## **状態を判定するメソッドを作っていこう**
```cpp
// 聞かれた状態と一致する？
bool is(char flag) 
{
	return (mFlag == flag); // 完全一致なら比較するだけだね。
}

// 聞かれた状態のいずれかが該当する？
bool either(char flag) {
	return (mFlag & flag) != 0; // 論理積の結果が0じゃないときだね
}

// 健康です。(悪いとこない)
bool isHealthy() {
	return is(0);
}
```

# <font color="#009F8C">**使うときのイメージ**</font>

## **麻痺らせたり、毒らせたり**
```cpp
// 状態を用意。
State state;

state.on(State::Kind::Palsy);  // 麻痺らせたぜ
state.on(State::Kind::Poison); // 毒らせたぜ
```

## **一発でいろんな状態にしたり**
```cpp
// 麻痺、毒、睡眠状態にしてやったぜ(bitなので論理和できる)
state.on(State::Kind::Palsy | State::Kind::Poison | State::Kind::Sleep);

// 全ステータス異常になぁれ
state.on(State::Kind::All);
```

## **状態を治してあげたり**
```cpp
state.off(State::Kind::Palsy);  // 麻痺解除だぜ
state.off(State::Kind::Poison); // 毒解除だぜ

// 万能薬のときはこれ。
state.reset();
```

## **状態を確認してあげたり**
```cpp
// 毒ですね？毒以外は大丈夫ですね？
if(state.is(State::Kind::Poison)) {
  // はい、毒だけです。。。
}

// 毒か麻痺か睡眠かのいずれかにかかってますね？
if(state.either(State::Kind::Poison | State::Kind::Palsy | State::Kind::Sleep)){
  // はい、どれかはわからないですがどれかにかかっています。。。
}

// めっちゃ元気ですか？悪いとこないですか？
if(state.isHealthy()){
  // Yes! I'm Healthy
}
```

## **新しい状態異常を追加したり**

```diff

struct Kind {
	enum {
		Palsy  = 1 << 0, // 00000001 麻痺
		Poison = 1 << 1, // 00000010 毒
		Sleep  = 1 << 2, // 00000100 睡眠
		Anger  = 1 << 3, // 00001000 怒り
		Sad    = 1 << 4, // 00010000 悲しみ
+		Burn   = 1 << 5, // 00100000 火傷
		All    = 0xFF,   // 11111111 全て
	};
};
```

列挙型に追加するだけで済むね。


# <font color="#009F8C">**いい感じ？**</font>

だいぶ長くなりましたが、使いやすい状態クラスが出来てきました。
これにて完成！

としたいところですが、ただ状態をクラスにしただけで
オブジェクト思考的としてはまだ考える余地があります。


# <font color="#009F8C">**状態クラスをよく見てみよう**</font>

![キャプチャ4.PNG](https://qiita-image-store.s3.amazonaws.com/0/57524/3858e687-b6ab-143f-28c0-4d9ef0655d4f.png)

状態クラスはこんな感じです。
状態の種類はこんなのがあるよーっていう部分と
状態を覚えさせるためのスイッチ(フラグ)部分です。

正直このスイッチ部分って<font color="red">**他でも使えると思いませんか？**</font>


# <font color="#009F8C">**状態とスイッチを分離しよう。**</font>

いままで状態クラスが持っていたBitの操作をしている部分を抜き出して
BitFlagを操作するだけのモノを作りました。

```cpp:BitFlag(スイッチ)クラス
// BitFlagクラス
class BitFlag 
{
private:
	char mFlag;

public:
	// フラグリセット
	void reset() {
		mFlag = 0; // 0にするだけ
	}

	// BITフラグON
	void on(char flag) 
	{
		mFlag |= flag; // 論理和をとればいいね。
	}

	// BITフラグOFF
	void off(char flag) 
	{
		mFlag &= ~flag; // 反転させたBITと論理積とればいいね。
	}

	// 指定されたフラグと一致する？
	bool is(char flag) 
	{
		return (mFlag == flag); // 完全一致なら比較するだけだね。
	}

	// 指定されたフラグのいずれかが一致する？
	bool either(char flag) {
		return (mFlag & flag) != 0; // 論理積の結果が0じゃないときだね
	}
};
```

状態クラスの方は今まで行っていたBITの直接操作がなくなりました。

```cpp:状態クラス
class State 
{
private:
	BitFlag mFlag;

public:
	// コンストラクタ
	State() :mFlag() {}

// 状態の種類(ビットシフトを使うとわかりやすいね)
struct Kind {
	enum {
		Palsy  = 1 << 0, // 00000001 麻痺
		Poison = 1 << 1, // 00000010 毒
		Sleep  = 1 << 2, // 00000100 睡眠
		Anger  = 1 << 3, // 00001000 怒り
		Sad    = 1 << 4, // 00010000 悲しみ
		All    = 0xFF,   // 11111111 全て
	};
};

	// ここから色んなメソッドを作っていくよ。
	void reset()           { mFlag.off(Kind::All);      } // 状態をリセット(健康にする)
	void on(char flag)     { mFlag.on(flag);	        } // 指定された状態をON(有効)にする
	void off(char flag)    { mFlag.off(flag);           } // 指定された状態をOFF(無効)にする
	bool is(char flag)     { return mFlag.is(flag);     } // 聞かれた状態と一致する？
	bool either(char flag) { return mFlag.either(flag); } // 聞かれた状態のいずれかが該当する？
	bool isHealthy()       { return mFlag.is(0);        } // 健康です。(悪いとこない)
};
```

こうする事でスイッチ(BITフラグ関連の処理)と状態が分離されます。
スイッチは状態以外でも使えるモノになりました。

# <font color="#009F8C">**まとめ**</font>

ちょっと長くなりすぎました。
BITフラグを例にしてしまったのは失敗だったかも。

とはいえ、始まりはいくつかのbool変数しかなかったものが
最終的にここまで肥大化したわけです。

おそるべしオブジェクト思考、おそるべしプログラム。
しかしこれがプログラムの面白さであり、醍醐味であるとも思います。

**1. 複数のフラグで状態管理する。**
**2. いろいろ管理がめんどくさいぞ！**
**3. 状態管理はBITフラグ使うとスマートに書けるじゃないの**
**4. 状態管理とBITフラグってそれぞれ別で考えられるよね**
**5. 状態管理するモノとBITを管理するモノを分けよう！**

これまた状態管理するモノとBITを管理するモノってのが
オブジェクトなの？って感じですよね。
でもこれもオブジェクト思考。

お粗末様でした。