1戦闘のターン制を完成させる|Unityで学ぶ自分の番と相手の番

初心者向け実践開発

※本記事にはアフィリエイトリンクが含まれています。
Amazonのアソシエイトとして、ゲームのタネは適格販売により収入を得ています。

▶ この回の完成形を、この場で遊ぶ
ブラウザで動く『1戦闘ターン制』です。Unity 6.3 LTS 完成プロジェクトと同じルール(あなた HP20/敵 HP24、こうげき5、敵こうげき4、ぼうぎょで次の被ダメージ半分)です。

1戦闘ターン制 はじめるでスタート
このブラウザではキャンバスを表示できません。

記事中の C#・ダウンロード ZIP と同じルールです。はじめる(または R)で開始。自分の番だけ「こうげき」「ぼうぎょ」。

Unity の完成プロジェクト
上で遊んでいるものと同じゲームです。ZIP を解凍し、Unity 6.3 LTS(6000.3) でフォルダを開いて Assets/Scenes/Main.unity を Play。
unity-turn-battle.zip をダウンロード

はじめに

「ボタンは付けた。でも、相手の番なのに押せてしまう…どう止めるの?」——そんな疑問向けです。

この記事では、Unity 6.3 LTS(エディタ表記 6000.3) で、1戦闘だけのターン制バトルを完成させます。敵の HP を 0 にすれば勝ち、自分の HP が 0 なら負け。「もう一度」や R キーで何度でもやり直せます。

今週身につけること:自分の番/相手の番という状態。 BattleState という列挙型(enum)で Ready → PlayerTurn → EnemyTurn → Win / Lose を持ちます。こうげき・ぼうぎょのボタンは PlayerTurn のときだけ受け付けます。敵の行動は EnemyTurn に入ってから進めます。勝敗も同じ状態のひとつです。

前回(第3回)は Godot でパイプの到達判定でした。今週はエンジンを Unity だけに切り替え、RPG の「誰の番か」を学びます。

完成形と、やらないこと

  • 作るもの: 1対1の戦闘。HP・こうげき・ぼうぎょ・敵の自動こうげき。勝ち/負け/やり直し。Unity ZIP と、同じルールのイン記事プレイ。
  • やらないこと: 複数敵、スキルツリー、セーブ、マップ移動、アニメ本格化、Godot 併記。
  • 持ち帰る1アイデア: 「入力ができる/できない」を if の散らばりで書かず、いまの状態ひとつでゲートする。
1戦闘ターン制の開始画面。はじめるボタンと双方のHP
完成形の開始画面(イン記事プレイ/ZIP と同じルール)。
ReadyからPlayerTurnとEnemyTurnを経てWinまたはLoseへ進む状態図
今週の核:BattleState(自分の番/相手の番/勝敗)。

準備(Unity 6.3 LTS)

  1. Unity 6 の公式ページから Hub 経由で 6.3 LTS(6000.3) を入れます。第2回と同じ系統です。
  2. 上の unity-turn-battle.zip を解凍し、Hub でそのフォルダを開きます。自分で組む場合は 2D(または空)の新規プロジェクトでも構いません。
  3. メインシーンは Assets/Scenes/Main.unity。ルート付近の TurnBattle にスクリプトが付いています。空の GameObject に TurnBattle.cs を付けても、Awake で UI を生成して動きます。
  4. Active Input HandlingBoth(Project Settings → Player)にしておきます。この回は Input.GetKeyDown で R キーを見ます。

初心者がハマりやすい点: 相手の番でもボタンが押せるままになることです。見た目で消すだけでなく、interactable = false にするか、メソッドの先頭で state != PlayerTurn なら return します。どちらか一方だけだと「見た目は消えたのにキーで動く」などが起きやすいです。

Mainシーン、TurnBattle、Canvas、EventSystem、BattleStateの流れ
入力・シーン構造。状態がボタンの有効/無効を決める。

ルール(数字は記事・ZIP・プレイで共通)

  • あなた HP 20/敵 HP 24
  • こうげき:敵に 5 ダメージ
  • ぼうぎょ:次に受けるダメージを半分(端数は切り上げ。敵の4なら2)
  • 敵の番:必ずこうげき 4(状態が EnemyTurn のときだけ)
  • 敵 HP ≤ 0 で勝ち、自分 HP ≤ 0 で負け
プレイヤーの番。こうげきとぼうぎょボタン、HP表示
途中:自分の番。パネル色とボタンで「誰の番か」が分かります。

スクリプト1:BattleFighter.cs(HPとダメージ)

ファイル名:Assets/Scripts/BattleFighter.cs。MonoBehaviour ではなく、普通の C# クラスです。TurnBattle から new して使います。

using UnityEngine;

/// <summary>
/// 1戦闘の参加者。名前・最大HP・現在HP・ダメージ処理だけを担当。
/// </summary>
public class BattleFighter
{
    public string DisplayName { get; private set; }
    public int MaxHp { get; private set; }
    public int Hp { get; private set; }

    public BattleFighter(string displayName, int maxHp)
    {
        DisplayName = displayName;
        MaxHp = maxHp;
        Hp = maxHp;
    }

    public bool IsAlive => Hp > 0;

    /// <summary>ダメージを受け、実際に減った量を返す。</summary>
    public int TakeDamage(int amount)
    {
        if (amount < 0)
            amount = 0;
        int before = Hp;
        Hp = Mathf.Max(0, Hp - amount);
        return before - Hp;
    }

    public void ResetHp()
    {
        Hp = MaxHp;
    }
}

別ゲームで変えるところ: コンストラクタの名前と最大 HP。回復を足すなら Heal メソッドを同じ場所に追加すると分かりやすいです。

スクリプト2:状態機械の核(自分の番だけ入力)

完成版の全文は ZIP の TurnBattle.cs(UI 生成付き)です。ここでは今週の学習点だけを抜き出した核を示します。考え方は完成版と同じです。

using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 記事用の核だけ抜粋。完成版の全文は ZIP の TurnBattle.cs(UI生成付き)です。
/// 空の GameObject に TurnBattle.cs を付けて Play してください。
/// </summary>
public class TurnBattleCore : MonoBehaviour
{
    const int PlayerAttack = 5;
    const int EnemyAttack = 4;

    public enum BattleState
    {
        Ready,
        PlayerTurn,
        EnemyTurn,
        Win,
        Lose
    }

    BattleState state = BattleState.Ready;
    // 完成版では BattleFighter を使います
    int playerHp = 20;
    int enemyHp = 24;
    bool defending;
    public Button attackButton;
    public Button defendButton;

    public void OnAttack()
    {
        // 自分の番以外は何もしない=入力のゲート
        if (state != BattleState.PlayerTurn)
            return;

        enemyHp = Mathf.Max(0, enemyHp - PlayerAttack);
        defending = false;
        if (enemyHp <= 0)
        {
            state = BattleState.Win;
            RefreshButtons();
            return;
        }
        BeginEnemyTurn();
    }

    public void OnDefend()
    {
        if (state != BattleState.PlayerTurn)
            return;
        defending = true;
        BeginEnemyTurn();
    }

    void BeginEnemyTurn()
    {
        state = BattleState.EnemyTurn;
        RefreshButtons();
        // 公式: MonoBehaviour.Invoke — 少し待ってから敵行動
        Invoke(nameof(EnemyAct), 0.55f);
    }

    void EnemyAct()
    {
        if (state != BattleState.EnemyTurn)
            return;
        int raw = EnemyAttack;
        int dmg = defending ? Mathf.CeilToInt(raw * 0.5f) : raw;
        playerHp = Mathf.Max(0, playerHp - dmg);
        defending = false;
        if (playerHp <= 0)
        {
            state = BattleState.Lose;
            RefreshButtons();
            return;
        }
        state = BattleState.PlayerTurn;
        RefreshButtons();
    }

    void RefreshButtons()
    {
        bool playerTurn = state == BattleState.PlayerTurn;
        // uGUI Button.interactable:自分の番だけ押せる
        if (attackButton != null)
            attackButton.interactable = playerTurn;
        if (defendButton != null)
            defendButton.interactable = playerTurn;
    }
}

別ゲームで変えるところ: 状態の名前(例:QuizTurn / RevealTurn)、ボタンの数、敵行動の中身。Invoke の待ち時間や、待ちなしで即 EnemyAct() でも構いません。

ボタン自体は uGUI の Button(公式パッケージマニュアル)です。onClickOnAttack / OnDefend を繋ぎます。完成版ではコードから onClick.AddListener しています。

スクリプト3:完成版の付け方(ZIP と同じ)

  1. BattleFighter.csTurnBattle.csAssets/Scripts/ に置く。
  2. 空の GameObject を作り、名前を TurnBattle にして TurnBattle をアタッチ。
  3. Play。カメラ・Canvas・EventSystem・ボタンは Awake で用意されます。
  4. R キーは Input.GetKeyDown(Unity 6.3 のスクリプトリファレンス)で見ています。敵の待ちは MonoBehaviour.Invoke です。

Play で確認すること

  1. はじめる後、自分の番だけ「こうげき」「ぼうぎょ」が押せる。
  2. こうげきすると敵 HP が 5 減り、すぐに敵の番へ。ボタンが押せなくなる。
  3. ぼうぎょすると、次の敵こうげきが 4→2 になる(1回だけ)。
  4. 敵を倒すと勝ち表示。「もう一度」または R で Ready に戻る。
勝利画面。敵HP0ともう一度ボタン
クリア(勝ち)画面。失敗時も同じ「もう一度」導線です。

つまづきポイント(1つ)

敵の番なのに、もう一度こうげきできてしまう。 OnAttack の先頭ゲートを書き忘れたか、RefreshButtonsinteractable を戻し忘れています。Play 中に Inspector で状態用の表示(完成版はメッセージ文言)が「敵の番」のとき、ボタンが灰色になっているかを見ます。

できたこと/身についたこと

  • 1戦闘のターン制(目標・失敗・やり直し付き)を Unity 6.3 で完成させた。
  • 自分の番/相手の番という状態で入力と敵行動を切り替えた。
  • HP 処理を BattleFighter に分け、進行役 TurnBattle と役割を分けた。

使い回しメモ

  • スニペット: BattleState + 「自分の番以外は return」+ ボタンの interactable
  • アイデア: 入力可否は散らばった if より、状態の一文で決める
  • 第二の用途: クイズの出題/正解表示、カードゲームのドロー/プレイ、対話の選択肢表示中ロック

次の自分のゲームで使うなら

メニューの「決定できるとき/演出中」も同じです。MenuState を用意し、演出中はボタンを閉じます。ジャンルが違っても「いま何の番か」は転用できます。

次に足してよい一言

クリティカル(たまにダメージ増)や、敵が「ためる」状態を1つ足すと、同じ状態機械のまま幅が出ます。まだマップやパーティは増やさなくて大丈夫です。

前回:『雨まで10秒』をGodotで完成させる|パイプ回転と水の到達判定

関連商品(PR)

今週の「Unity で状態を書く」学習に沿いやすいものを選びました。価格は掲載時点で変動するため、リンク先でご確認ください。

参考

コメント

タイトルとURLをコピーしました