前回は戦闘時に敵を複数出現させる処理を実装しました。今回はプレイヤーと敵が速さ順に行動するように処理を実装していきたいと思います。
その前に、現状敵を撃破した時にの演出で、敵の画像と一緒に敵のUIも一緒に拡大するようになってしまっているため、修正します。
EnemyManagerに以下の記述を追加します。
[Header("敵の画像")]public SpriteRenderer enemyImage;
次に敵のプレハブを開き、一番上の階層のGameObjectのSpriteRendererコンポーネントを削除し、そのGameObjectの階層下に空のGameObjectを作成し、名前をImageにします。ImageにSpriteRendererコンポーネントを追加し、SpriteRendererのSpriteに表示させる敵画像を割り当てます。最後に、"Image"オブジェクトを、プレハブの最上位のオブジェクトのEnemyManagerコンポーネントのEnemyImageに割り当てて、プレハブを保存します。
ついでに、BoxCollider2Dの判定の大きさが画像に対してかなり大きかったので画像に合わせておきました。もともと敵をタップした時にindexの割当がうまくいってないかのような挙動をしていたのですが、これで修正できました。
次にBattleManager.csを開き、以下の部分を変更します。
クリックで展開
IEnumerator EnemyDeath(int enemyIndex)
{
yield return new WaitForSeconds(0.5f);
SoundManager.instance.PlaySE(3);
enemysList[enemyIndex].enemyImage.transform.DOScale(new Vector3(2.0f, 2.0f, 2.0f), 1f);
enemysList[enemyIndex].enemyImage.DOColor(new Color(1f, 0f, 0f, 0f), 1f);
yield return new WaitForSeconds(2f);
DialogTextManager.instance.SetScenarios(new string[] {
enemysList[enemyIndex].name + "を倒した" });
Debug.Log("enemysList[targetIndex]のオブジェクト破壊");
Destroy(enemysList[targetIndex].gameObject);
enemysList.Remove(key: targetIndex);
Debug.Log("enemysListの要素数は" + enemysList.Count);
DeletAgiDicElemDeathEnemy(targetIndex);
Debug.Log("AgiDicの要素数は" + AgiDic.Count);
}
デバッグ用のメッセージ表示も一緒にしまっていますがご容赦ください。EnemyDeath関数内の死亡時の演出(1秒かけてサイズを2倍に、色をだんだん赤くする処理)を敵のImageのみに適用するようにします。enemysList[enemyIndex].enemyImage.transform.DOScaleで敵画像のみサイズを変え、enemysList[enemyIndex].enemyImage.DOColorで敵画像のみ色を変えられます。UIのサイズは変わりません。
バグの修正を終えたら、次は戦闘時の行動順の処理を実装していきたいと思います。前回の記事では中途半端に処理を書いてしまっていたので、戦闘時の挙動がバグだらけなので、それが正常に動くようにします。BattleManegerの以下の部分を修正します。
クリックで展開
public void CalcAttackOrder()
{
player.CalcAGI(player.agi);
enemysList[0].CalcAGI(enemysList[0].agi);
enemysList[1].CalcAGI(enemysList[1].agi);
enemysList[2].CalcAGI(enemysList[2].agi);
AgiDic[0] = player.battleAgi;
AgiDic[1] = enemysList[0].battleAgi * UnityEngine.Random.Range(-30, 31);
AgiDic[2] = enemysList[1].battleAgi * UnityEngine.Random.Range(-30, 31); ;
AgiDic[3] = enemysList[2].battleAgi * UnityEngine.Random.Range(-30, 31); ;
}
この関数はプレイヤーと敵の行動順を計算する関数です。この書き方だと、敵が1体、2体の場合はenemysList[2](Dictionary)の範囲外アクセスになってしまいます。3体の場合でも敵を1体倒した後に
その倒した敵のenemyListにアクセスしようとするとエラーになります。なので、enemyListにアクセスする前にenemysList.ContainsKey(index)で指定したindexのKeyが存在するかを判定し、存在した場合のみenemysListにアクセスするようにします。
修正したものが以下になります。
クリックで展開
public void CalcAttackOrder()
{
Debug.Log("行動順の計算");
AgiDic.Clear();
player.CalcAGI(player.agi);
AgiDic[0] = player.battleAgi;
for(int i = 0;i < 3; i++)
{
if (enemysList.ContainsKey(i))
{
enemysList[i].CalcAGI(enemysList[i].agi);
AgiDic[i + 1] = enemysList[i].battleAgi + (int)(enemysList[i].battleAgi * (UnityEngine.Random.Range(-0.3f, 0.3f)));
}
}
Debug.Log("速さのリストを表示");
Debug.Log("playerの速さ" + AgiDic[0]);
for(int i = 0; i < 3; i++)
{
if (enemysList.ContainsKey(i))
{
Debug.Log("enemyindex" + i + "の速さ" + AgiDic[i + 1]);
}
}
}
ついでに速さのDictionaryに格納する際に、バトラーの速さ+-30%~30%するようにしました。
この関数をBattleManager.csのTurnProcess関数の中で呼び出します。
クリックで展開
IEnumerator TurnProcess()
{
CalcAttackOrder();
while (true)
{
Debug.Log("ターン開始");
yield return new WaitForSeconds(0.1f);
int actionIndex = ReadAgiDic();
if (actionIndex == 0)
{
Debug.Log("プレイヤーの攻撃開始");
yield return StartCoroutine(StartPlayerAttack());
}
else if (1 <= actionIndex && actionIndex <= 3)
{
yield return StartCoroutine(EnemyTurn(actionIndex - 1));
}
DeleatAgiDicMaxElem(actionIndex);
yield return new WaitForSeconds(0.1f);
if (AgiDic.Count == 0)
{
Debug.Log("行動全て処理終わり");
yield return new WaitForSeconds(0.2f);
TurnEnd();
yield return new WaitForSeconds(0.1f);
Debug.Log("TurnProcessを抜ける");
break;
}
}
}
TurnProcess関数の一番最初にCalcAttackOrder関数を呼び出し、行動順リストを作成し、その後while(true)でループ処理をさせます。ループ処理内で、ReadAgiDic関数で行動順リストの速さが一番大きいValueのKeyのindexを読み込み、actionIndexに格納します。actionIndexで処理を分岐させ、StartPlayerAttackもしくはEnemyTurnのコルーチンを開始します。それぞれのコルーチンには、呼び出しの際、頭にyield return を付けるようにしました。これでこのコルーチンの処理が終わるまでTurnProcess側の処理を待機させられるようになります(StartPlayerattackとTurnProcessが並列処理にならなくなる)。
StartAttack関数、もしくはEnemyTurnのコルーチンが終了したら、DeleatAgiDicMaxElem関数で処理をしたactionIndexのKeyの要素をAgiDicから削除します。前回の記事ではこの関数は引数をとらず、ReadAgiDic関数と同じように、AgiDic内のvalueが最大値のKeyの削除を行っていたのですが、それだとバグを発生させる可能性があるかもしれないと思い、actionIndexを引数に持たせて、DeleatAgiDicMaxElem(int key)関数内でAgiDic.Remove(key)としています。これでプレイヤーと敵が速さ順に行動するようになりました。
youtu.be
次回は戦闘勝利時、死亡時の処理を実装していきたいと思います。