現場レベルのゲーム制作が、すべてここで学べます。
この記事はノンフィールドRPGの作り方講座の第11回です。
前回はバトルシーンでの敵キャラの表示やステータスウィンドウの表示を実装、そしてスキル使用時の処理を実装しました。
前回の記事:

第11回ではスキルボタンをタッチして主人公ターンと敵キャラターンの進行の実装およびバトル中の効果音の準備を行っていきます。
スキルボタンオブジェクトにButtonコンポーネントを追加する
3つのスキルボタンに[Button]コンポーネントを追加します。
HierarchyビューにあるButton1オブジェクトをクリックし、Shiftキーを押しながらButton3までオブジェクトをクリックして、3つのButtonオブジェクトをまとめて選択します。

Inspectorビューを表示し、下にあるAdd Comopnentをクリックして、検索バーに[Button]と入力します。一覧に表示されたButtonコンポーネントをクリックして追加します。

On Click()のイベントリスナーの設定は後ほど行います。
バトルシステムを作り上げる スキル使用時の処理とターン進行処理を実装
このゲームではターン制戦闘システムを採用しており、プレイヤー側は攻撃、防御、回復の3つの行動を選択できます。回復スキルは使用した後も行動できるシステムにします。
ですが、毎ターン回復できるわけではなく、一度回復スキルを使うと数ターンのスキル使用ができない設定にします。こうした行動制限を加えることでいつ回復スキルを発動させるかの選択をプレイヤーに迫り、シンプルな構造ながらも戦略性が生まれます。
この回復スキルの制限ターン数とターン切り替えの待ち時間をまず定数クラスに追加します。
GameConstants.cs
|
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 |
using UnityEngine; public class GameConstants { public const string SaveDataKey = "HATENAKIDUNGEON";// データ保存で使うキー public const int StartLevel = 1;// ゲーム開始時のレベル public const int StartFood = 100;// ゲーム開始時の食料 public const int InitialFloor = 1;// 最初の階層 public const int HeroBaseHp = 45;// プレイヤーのHP public const int HeroRateHp = 5;// プレイヤーのレベル1ごとの上昇HP public const int EnemyBaseHp = 8;// 敵キャラのHP public const int EnemyRateHp = 3;// 敵キャラのレベル1ごとの上昇HP public const int HeroBaseAttack = 3;// プレイヤーの攻撃力 public const int HeroRateAttack = 3;// プレイヤーのレベル1ごとの上昇攻撃力 public const int EncountRate = 50;// 敵キャラとの遭遇率(%) public const float MoveSpeed = 3f;// 前進の処理の早さ public const float MoveDistance = 3f;// 前進した時のカメラの移動距離 public const float RecoveryRate = 0.2f;// HPの回復量 public const int RecoveryCT = 3;// 回復スキルのクールタイム public const float TurnSpeed = 1f;// ターンの処理の待ち時間 } |
バトルシステムをコントロールしているのは前回作成したBattleDirector.csです。
ここからは戦闘システムがちゃんと動くように処理を実装していきます。
BattleDirector.cs
まず、スキルボタンオブジェクトをアタッチするための変数と、スキルボタンにスキルの威力を表示するためのテキストオブジェクトをアタッチするための変数をこのスクリプトに追加します(威力を表示するのは攻撃スキルと回復スキルだけ)。
次に、スキルの効果を発動させるためのメソッドUseSkillを追加します。UseSkillメソッドは引数に、スキルの使用者、スキルの対象者、スキルコード(文字列)、スキル使用後の処理を引数として渡すようにしています。スキルコードによって条件が分岐しスキルの効果を発動するようにします。
また、主人公のターンと敵キャラのターンが回ってきたときのメソッドを追加し、スキルボタンをタッチしたときにスキル効果を発動する処理も追加します。
|
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
using UnityEngine; using TMPro; using Unity.Collections; using UnityEngine.Events; using System.Collections;// IEnumerator型を使用するために必要 public class BattleDirector : MonoBehaviour { public SpriteRenderer EnemySprite; public StatusWindowController StatusController; public StatusWindowController EnemyStatusController; public GameObject AttackCommand;// Button1(攻撃)のオブジェクト public TextMeshProUGUI AttackPower;// Button1のパワーの表示 public GameObject DefenseCommand;// Button2(防御)のオブジェクト public GameObject RecoveryCommand;// Button3(回復)のオブジェクト public TextMeshProUGUI RecoveryPower;// Button3のパワーの表示 // Start is called before the first frame update void Start() { if (GameManager.Instance == null) { GameManager.LoadScene(SceneCode.Title); return; } SoundManager.Instance.PlayBattleBGM(); GameManager.Instance.PlayRecord.SceneCode = SceneCode.Battle; // エンカウントしたら敵キャラを作成する(ロードされたバトルの続きなら処理しない) if (GameManager.Instance.PlayRecord.EnemyStatus == null || string.IsNullOrEmpty(GameManager.Instance.PlayRecord.EnemyStatus.ID)) { EnemyParams randomEnemy = GameManager.Instance.EnemyData.GetRandomEnemyParams();// ランダムで敵キャラのデータを取得 int enemyLevelBonus = GameManager.Instance.PlayRecord.DungeonStatus.Floor - 1;// 敵キャラの強さの上昇量(レベルアップ量) GameManager.Instance.PlayRecord.EnemyStatus = new UnitStatus(randomEnemy.ID, enemyLevelBonus);// 敵キャラを初期化 } EnemyParams enemyParams = GameManager.Instance.EnemyData.GetEnemyParams(GameManager.Instance.PlayRecord.EnemyStatus.ID); EnemySprite.sprite = enemyParams.EnemySprite;// 敵キャラ画像を表示 TurnHero();// 主人公のターンから始まる } // Update is called once per frame //void Update() //{ //} // ステータスウィンドウの更新 private void UpdateStatus() { StatusController.UpdateUnitStatus(GameManager.Instance.PlayRecord.HeroStatus);// 主人公のステータス StatusController.UpdateDungeonStatus(GameManager.Instance.PlayRecord.DungeonStatus);// ダンジョンのステータス EnemyStatusController.UpdateUnitStatus(GameManager.Instance.PlayRecord.EnemyStatus);// 敵キャラのステータス } // 主人公のターン private void TurnHero() { UpdateStatus();// ステータスウィンドウの更新 UnitStatus heroStatus = GameManager.Instance.PlayRecord.HeroStatus;// 主人公のデータを参照 heroStatus.IsGuard = false;// ガード状態を初期化 heroStatus.ProgressCT();// CTを進める AttackCommand.SetActive(true);// スキルボタンの表示 DefenseCommand.SetActive(true); RecoveryCommand.SetActive(heroStatus.CT == 0);// CTが回復していたら表示 AttackPower.text = heroStatus.GetAttack().ToString();// 攻撃力の表示 RecoveryPower.text = heroStatus.GetRecovery().ToString();// 回復力の表示 } // 敵キャラのターン private void TurnEnemy() { UpdateStatus();// ステータスウィンドウの更新 UnitStatus heroStatus = GameManager.Instance.PlayRecord.HeroStatus;// 主人公のデータを参照 UnitStatus enemyStatus = GameManager.Instance.PlayRecord.EnemyStatus;// 敵キャラのデータを参照 UseSkill(enemyStatus, heroStatus, "Attack", TurnHero); } // スキル処理(スキルの使用者、スキルの対象者、スキルコード、次の処理を引数として渡す) private void UseSkill(UnitStatus user, UnitStatus target, string skillCode, UnityAction nextTurn) { StartCoroutine(BootSkill()); IEnumerator BootSkill() { switch (skillCode) { case "Attack": yield return new WaitForSeconds(GameConstants.TurnSpeed);// TurnSpeedの時間を待ってから処理 if (!target.IsGuard)// 対象者がガードしていなければダメージを与える { target.SetDamage(user.GetAttack()); } if (nextTurn != null) nextTurn();// 次の処理があればメソッドを呼び出す break; case "Defense": yield return new WaitForSeconds(GameConstants.TurnSpeed); user.IsGuard = true; // ガードを有効にする if (nextTurn != null) nextTurn();// 次の処理があればメソッドを呼び出す break; case "Recovery": SoundManager.Instance.PlayRecoverySE(); user.Recovery(); // 回復する user.CT = GameConstants.RecoveryCT;// CTの設定 if (nextTurn != null) nextTurn();// 次の処理があればメソッドを呼び出す break; } UpdateStatus(); } } // 攻撃スキルをタッチしたときの処理(食料を消費する。ターンも進む。) public void TouchButton1() { SoundManager.Instance.PlayTouchSE(); AttackCommand.SetActive(false);// スキルボタンを全て非表示にする DefenseCommand.SetActive(false); RecoveryCommand.SetActive(false); GameManager.Instance.PlayRecord.DungeonStatus.Food--; UseSkill(GameManager.Instance.PlayRecord.HeroStatus, GameManager.Instance.PlayRecord.EnemyStatus, "Attack", TurnEnemy); } // 防御スキルをタッチしたときの処理(食料を消費する。ターンも進む。) public void TouchButton2() { SoundManager.Instance.PlayTouchSE(); AttackCommand.SetActive(false);// スキルボタンを全て非表示にする DefenseCommand.SetActive(false); RecoveryCommand.SetActive(false); GameManager.Instance.PlayRecord.DungeonStatus.Food--; UseSkill(GameManager.Instance.PlayRecord.HeroStatus, GameManager.Instance.PlayRecord.EnemyStatus, "Defense", TurnEnemy); } // 回復スキルをタッチしたときの処理(食料を消費しない。ターンも進まない。) public void TouchButton3() { SoundManager.Instance.PlayTouchSE(); RecoveryCommand.SetActive(false);// 回復ボタンだけ非表示 UseSkill(GameManager.Instance.PlayRecord.HeroStatus, GameManager.Instance.PlayRecord.EnemyStatus, "Recovery", null); } } |
現在BattleDirectorは戦闘の進行管理、UIの表示制御、効果音トリガーの設定の3つの役割を持っています。ダメージ計算やパラメータの数値処理はUnitStatus側で行います。
|
1 |
private void UseSkill(UnitStatus user, UnitStatus target, string skillCode, UnityAction nextTurn) |
↑今回このメソッドの第4引数はUnityAction型(C#ではデリゲートと呼ばれる)で定義されていて、これによりメソッドを引数としてUseSkillに渡して処理を行わせることができます。
ここでは回復スキルの後の処理をnullにし、攻撃と防御スキルの後の処理をTurnEnemyにすることで回復スキルの後は連続行動を可能にしています。
デリゲートとイベントとUnityActionの使い方について詳しく知りたい方はこちら>>

また、ここではUseSkill内のコルーチンは「時間経過をまたいで処理を進める」ために使われています(yield return new WaitForSeconds(GameConstants.TurnSpeed)の部分)。
戦闘シーンでは攻撃や防御の演出時間を確保しつつ、その後にダメージ適用やターン遷移を行いたい場合があります。
yield return new WaitForSeconds() を使えば、ゲーム全体を止めずに「間」を演出できます。
このようにコルーチンを使うと、非同期処理をスクリプト内で手順通りに自然な形で記述できます。
これでバトルシーンのターン制の流れができました。
スキルボタンをスクリプトにアタッチする
BattleDirectorにスキルボタンを表示・非表示できるようにアタッチしておきます。またスキルの威力を表示するためにText(TMP)オブジェクトもアタッチしておきます。

なお、防御スキルは敵の攻撃を無効にするという内容なので、威力を表示しないようにしています。

スキルボタンにイベントリスナーを追加してメソッドを設定する
スキルボタンをタッチしたときに攻撃、防御、回復のスキルが発動するようにします。
まとめ

ここまででバトルシーンでの主人公と敵キャラのターンの流れ、そしてバトル中のオーディオの準備を実装しました。
次回からは攻撃時や被ダメージ時の演出、バトル勝利・敗北時の処理、さらに勝利時のレベルアップ処理を実装していきます。
次の記事:

現場レベルのゲーム制作が、すべてここで学べます。






コメント