【Unity】ポケモンのようなターン制ゲームを作ってみた 〜ターン終了時の処理〜

ポケモンのようなターン制のゲームを開発してまして、そこで上手く実装できた部分の書き方を共有します。続いたり続かなかったり。今回はターン終了時の処理です。

#概要
- ポケモン風ターン制ゲームとは
- ポケモンにおけるターン終了時の処理
- StateMachine (ステートマシン)とは
- Coroutine (コルーチン)とは
- コードサンプル
- 解説

#ポケモン風ターン制ゲームとは
以下の特徴を持ったゲームです
- ターン開始時にお互いがわざ選択を行う
- キャラクターは素早さ順に行動する
- わざはダメージを与えたり、特殊効果を持つ
- キャラクターが先に全滅すると負け

#ポケモンにおけるターン終了時の処理
ざっとこんだけあります

```
1．天候解除(→ギフト/てんきや)→天候ダメージ/サンパワー/かんそうはだ/あめうけざら/アイスボディ
2．みらいよち/はめつのねがい
3．ねがいごと
4．だっぴ/うるおいボディ/いやしのこころ
　→くろいヘドロ/たべのこし/グラスフィールドの回復
5．アクアリング
6．ねをはる
7．やどりぎのたね 
8．どく/もうどく/ポイズンヒール 
9．やけど 
10．あくむ 
11．呪い 
12．しめつける状態解除
　→しめつける/まきつく/うずしお/マグマストーム/すなじごく/ほのおのうず/からではさむ/まとわりつく
13．あくび 
14．ほろびのうた 
15．ちょうはつ 
16．アンコール 
17．かなしばり 
18．でんじふゆう 
19．かいふくふうじ 
20．さしおさえ 
21．リフレクター 
22．ひかりのかべ 
23．しんぴのまもり 
24．しろいきり 
25．おいかぜ 
26．おまじない 
27．トリックルーム 
28．じゅうりょく 
29．みずあそび/どろあそび 
30．ワンダールーム 
31．マジックルーム 
32．グラスフィールド/エレキフィールド/ミストフィールド 
33．さわぐ
　→ナイトメア/しゅうかく/ものひろい/ムラっけ/どくどくだま/かえんだま/くっつきバリ

```

[引用元](http://g836.hatenablog.com/entry/2016/07/12/000000)


これらの詳しい効果について知る必要はありません。
重要なのは、各処理は行われる場合と行われない場合があり、処理が行われる場合には、「順番に」エフェクトやUI表示を行う必要があるということです。


#ステートマシンとは
ポケモンのようなターン制ゲームの場合、わざを選択するタイミング、わざを出すタイミング、ターン終了時の処理をするタイミングなど、ゲームの状態を見て進行を管理する仕組みが必要です。
そういった仕組みを「ステートマシン」と呼びます。詳しくは割愛するので知らない方は調べてみてください。


#コルーチンとは
簡単に言うと一定時間処理を止められる仕組みです。
今回で言えば、例えば毒状態で毒ダメージを受けている間は処理を止めておき、それが終わったら次は宿り木の種の処理を行う、といった制御が必要です。
そういった制御にピッタリなのがコルーチンで、これを使った実装方法を紹介します。


#コードサンプル
論点でない部分のコードは割愛しています。

```C#:TurnStateMachine.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TurnStateMachine : MonoBehaviour
{
	public Pokemon myPokemon;
	public Pokemon enemyPokemon;

	List<Pokemon> SortedPokemons = new List<Pokemon> ();

	/* ターン終了時の処理に関わる部分以外は割愛 ステートマシンで制御します*/

	void StartTurnEndProcessing()
	{
		//コルーチンを開始
		StartCoroutine(TurnEndProcessing());
	}

	//コルーチン処理
	IEnumerator TurnEndProcessing ()
	{
		//1.勝敗判定
		if (CheckAlive()) {
			yield break;
		}

		//2.同速チェックと並び替え
		bool isSame = Sort ();

		//3.天候解除
		if (WeatherProcessing ()) {
			yield return new WaitForSeconds (1f);
		}

		/* 中略 */

		//4.素早さが同じ場合は順番をシャッフルする
		Shuffle (isSame);

		//5.毒ダメージ
		foreach (var item in SortedUnits) {
			if (PoizonProcessing (item)) {
				yield return new WaitForSeconds (0.5f);
			}
		}

		/* 中略 */

		//6.勝敗判定
		if (CheckAlive()) {
			yield break;
		}

		//7.交代の有無を確認
		if(CheckChange()){
			//交代の処理
			CharacterChange();
		} else {
			//ターン開始に移る処理
			TurnStart();
		}
	}

	bool Sort ()
	{
		//myPokemonとenemyPokemonの素早さを比較
		//素早さが高い順にSortedPokemonsにAddする
		//素早さが同じ場合はtrue,それ以外はfalseを返す
	}

	void Shuffle (bool isSame)
	{
		//素早さが同じ場合は順番のシャッフルを行う
		//素早さが同じでない場合は何もしない
	}

	bool WeatherProcessing ()
	{
		//天候解除の有無を判定する
		//天候解除ありの場合はエフェクトやUI表示を更新してtrueを返す
		//それ以外はfalseを返す
	}

	bool PoizonProcessing (Pokemon pokemon)
	{
		//pokemonが瀕死状態ならfalseを返して終了する
		//pokemonが毒状態ならエフェクトと毒ダメージの処理を行う
		//それ以外はfalseを返す
	}

	bool CheckAlive()
	{
		//対戦の勝敗判定を行う
		//勝ちまたは負けの判定が出た場合はtrueを返す
		//それ以外はfalseを返す
	}

	bool CheckChange()
	{
		//交代の有無を判定して交代が必要な場合はtrueを返す
	}

	void CharacterChange()
	{
		//ポケモンの交代の必要があれば交代画面に映る
	}

	void TurnStart()
	{
		//ターン開始時に移る
	}
}
```

#解説
以下がポイントです。

- 最初に勝敗判定を行って、勝敗が決する場合は処理を行わない//1.参照
- 砂嵐ダメージなどポケモン毎に行う処理は、素早さ順で行われる。
　→最初に素早さ順でListに追加しておき、List内の順番に沿って処理する。//2.参照

- 素早さが同じ場合は処理毎に素早さ順の判定を行う。
　→素早さが同じ場合だけシャッフル処理を行う。//4.参照

- エフェクト表示やUI更新が必要な時だけ待機時間を設ける
　→個別の処理は処理の有無をbool値で返して、trueの時のみ待機する。//3,5.参照
　→コルーチンの待機時間は```yield return new WaitForSeconds (float);```で表せます。

- 毒ダメージなどでターン終了時の処理中にポケモンが瀕死になる場合があるため、最後の方で再度勝敗判定を行う。//6.参照



以上、マニアック気味の内容でした。
他のターン状態を管理する処理については割愛しています。

もし参考になった場合はいいねをお願いします。