見出し画像

『Unity』 簡単なシンプルターン制システムの作り方

どうもみなさん!Sonoraです。今回はUnityで簡単なターン制システムの作り方をご紹介していきたいと思います。悪魔で私個人のやり方なので、もっといい方法があるのに!と思う方もいらっしゃると思いますが、そこは配慮していただければ幸いです。この記事では基本コードのみとなりますのでUnityの使い方はご理解してる上で進めていきます。


キャラクターステータスクラス

まずターンシステムを作る前に必要なキャラクターステータスを作ります。
プレイヤーと敵で分けていきたいと思います。
プレイヤーと敵には『HP』、『攻撃力』、『防御力』と共通するステータスがあるので、ここら辺は継承を使って書きたいと思います。

using UnityEngine;

public class CharacterStatus
{
    //キャラクターステータス
    public int hp;
    public int attackPoint;
    public int defenctPoint;

    //攻撃関数
    public virtual void Attack(CharacterStatus attacker, CharacterStatus defencer)
    {
        defencer.hp -= CalcDamage(attacker, defencer);
    }

    //ダメージ計算
    public int CalcDamage(CharacterStatus attacker, CharacterStatus defencer)
    {
        float damageCalc = attacker.attackPoint - defencer.defenctPoint;
        float ratio  = 0;
        for(int i=0;i < 100; i++)
        {
            ratio = Random.Range(0.7f, 1f);
        }

        int damage = (int)(damageCalc *= ratio);
        if (damage < 0) damage = 0;

        return damage;
    }
}

MonoBehaviourは継承せずにただのクラスにします。
必要なキャラクターステータス、攻撃関数に関してはプレイヤーと敵で攻撃の仕方を変えるかもしれないので『virtual』にしてます。でも、今回の記事では書かなくても問題はありません。
次にダメージ計算に関しては、人それぞれ違うと思うので参考程度に見ていただければ幸いです。
これでキャラクターステータスクラスは完成です。

プレイヤークラス

続いてプレイヤークラスを作成していきます。
スクリプト名は『Player』にします。

public class Player : CharacterStatus
{
    public override void Attack(CharacterStatus attacker, CharacterStatus defencer)
    {
        base.Attack(attacker, defencer);
    }

    public Player(int hp, int attackPoint, int defenctPoint)
    {
        this.hp = hp;
        this.attackPoint = attackPoint;
        this.defenctPoint = defenctPoint;
    }
}

プレイヤークラスでは先程作成した『CharacterStatus』を継承しています。
こちらの『public override 何ちゃら』というのは書かなくてもOKです!
下の方はコンストラクタになります。インスタンスが生成された時に呼び出されるものです。
引数に設定された値がプレイヤーのステータスになるわけです!

敵クラス

続いて敵クラスになります。
スクリプト名は『Enemy』にします!

public class Enemy : CharacterStatus
{
    public override void Attack(CharacterStatus attacker, CharacterStatus defencer)
    {
        base.Attack(attacker, defencer);
    }

    public Enemy(int hp, int attackPoint, int defenctPoint)
    {
        this.hp = hp;
        this.attackPoint = attackPoint;
        this.defenctPoint = defenctPoint;
    }
}

やっていることは先程のプレイヤークラスと同じなので説明は割愛します。

バトルマネージャークラス

いよいよ今回の本題!ターンシステムを作っていきます!
スクリプト名は『BattleManager』にします。
結構長めのコード数になっております。
一つ一つ解説していきますので大丈夫です!

using System.Collections;
using UnityEngine;

public class BattleManager : MonoBehaviour
{
    //プレイヤーと敵を取得
    private Player player;
    private Enemy enemy;

    //負けフラグ
    private bool _lose = false;

    //バトルの状態を管理する変数
    enum BattleStatus
    {
        PlayerTurnStart,
        PlayerAttack,
        EnemyTurnStart,
        EnemyAttack,
    }
    //最初はプレイヤーからスタート
    BattleStatus currentStatus = BattleStatus.PlayerTurnStart;

    private void Start()
    {
        //キャラクターを生成
        player = new Player(100,30,20);
        enemy = new Enemy(40, 25, 14);

        //バトル開始
        StartCoroutine(Battle());
    }

    //バトルコルーチン
    IEnumerator Battle()
    {
        Debug.Log("バトル開始");

        //プレイヤーか敵どちらかのHPが0だったらループから抜ける
        while(true)
        {
            //プレイヤーターン
            yield return new WaitForSeconds(1f);
            ChangeTurn(BattleStatus.PlayerTurnStart);

            //プレイヤーの攻撃
            yield return new WaitForSeconds(1f);
            ChangeTurn(BattleStatus.PlayerAttack);
            if (BattleWin(enemy)) break;

            //敵のターン
            yield return new WaitForSeconds(1f);
            ChangeTurn(BattleStatus.EnemyTurnStart);

            //敵の攻撃
            yield return new WaitForSeconds(1f);
            ChangeTurn(BattleStatus.EnemyAttack);
            if (BattleLose(player))
            {
                _lose = true;
                break;
            }
        }

        yield return new WaitForSeconds(1f);

        //敵のHPが0だったら勝利
        if (!_lose)
        {
            Debug.Log("あなたは勝ちました");
        }
        //プレイヤーのHPが0だったら負け
        else
        {
            Debug.Log("あなたは負けました");
        }
    }

    //バトルターンの変更関数
    void ChangeTurn(BattleStatus battleStatus)
    {
        currentStatus = battleStatus;
        switch (currentStatus)
        {
            case BattleStatus.PlayerTurnStart:
                Debug.Log("PlayerTurn");
                break;
            case BattleStatus.PlayerAttack:
                player.Attack(player, enemy);
                Debug.Log("Playerの攻撃!");
                Debug.Log("Playerは" + "Enemyに" + player.CalcDamage(player, enemy) + "ダメージ与えた");
                break;
            case BattleStatus.EnemyTurnStart:
                Debug.Log("EnemyTurn");
                break;
            case BattleStatus.EnemyAttack:
                player.Attack(enemy, player);
                Debug.Log("Enemyの攻撃!");
                Debug.Log("Enemyは" + "Playerに" + enemy.CalcDamage(enemy, player) + "ダメージ与えた");
                break;
        }
    }

    //勝利関数
    bool BattleWin(CharacterStatus enemy)
    {
        if (enemy.hp <= 0)
        {
            enemy.hp = 0;
            return true;
        }
        else return false;
    }

    //負け関数
    bool BattleLose(CharacterStatus player)
    {
        if (player.hp <= 0)
        {
            player.hp = 0;
            return true;
        }
        else return false;
    }
}

プレイヤークラスの取得

    //プレイヤーと敵を取得
    private Player player;
    private Enemy enemy;

ここでは、プレイヤーと敵を取得しています。
これで、プレイヤーと敵を生成することができます。

負けフラグ

 //負けフラグ
 private bool _lose = false;

この変数はプレイヤーが負けたかを判定するフラグです。

現在のターンを管理

   //バトルの状態を管理する変数
    enum BattleStatus
    {
        PlayerTurnStart,
        PlayerAttack,
        EnemyTurnStart,
        EnemyAttack,
    }
    //最初はプレイヤーからスタート
    BattleStatus currentStatus = BattleStatus.PlayerTurnStart;

ここではターンの管理を列挙型を使って管理しています。
プレイヤーがターンをスタートした時のターン、
プレイヤーが攻撃する時のターン、
敵がターンをスタートした時のターン、
敵が攻撃する時のターン、
に分かれています。
最初はプレイヤーからスタートさせたいので、現在のターンをPlayerTurnStartに変更します(ここはいらないかも)。

キャラクター生成とバトルの開始

   private void Start()
    {
        //キャラクターを生成
        player = new Player(100,30,20);
        enemy = new Enemy(40, 25, 14);

        //バトル開始
        StartCoroutine(Battle());
    }

スタート関数では、キャラクター生成を最初に行っています。
プレイヤーのステータス、敵のステータスを決めてやります。
そして、バトルの開始になります。

バトルのターン変更関数

先にバトルコルーチンの説明の前にバトルターンの変更から説明していきます。

   //バトルターンの変更関数
    void ChangeTurn(BattleStatus battleStatus)
    {
        currentStatus = battleStatus;
        switch (currentStatus)
        {
            case BattleStatus.PlayerTurnStart:
                Debug.Log("PlayerTurn");
                break;
            case BattleStatus.PlayerAttack:
                player.Attack(player, enemy);
                Debug.Log("Playerの攻撃!");
                Debug.Log("Playerは" + "Enemyに" + player.CalcDamage(player, enemy) + "ダメージ与えた");
                break;
            case BattleStatus.EnemyTurnStart:
                Debug.Log("EnemyTurn");
                break;
            case BattleStatus.EnemyAttack:
                player.Attack(enemy, player);
                Debug.Log("Enemyの攻撃!");
                Debug.Log("Enemyは" + "Playerに" + enemy.CalcDamage(enemy, player) + "ダメージ与えた");
                break;
        }
    }

ここでは引数に与えられたターンを現在のターンに反映する関数です。
反映した後に、スイッチ文を使用し各ターンごとに処理を書いています。
一つ一つのターンの説明をしていきます。
PlayerTurnStart・・・ ログにPlayerTurnと表示
PlayerAttack.    ・・・ プレイヤーが敵に攻撃、その情報をログに表示(与えたダメージなど)。
EnemyTurnStart・・・ ログにEnemyTurnと表示
EnemyAttack.    ・・・ 敵がプレイヤーに攻撃、その情報をログに表示(与えたダメージなど)。

バトルコルーチン

バトルの処理をコルーチンを使って処理します。

    //バトルコルーチン
    IEnumerator Battle()
    {
        Debug.Log("バトル開始");

        //プレイヤーか敵どちらかのHPが0だったらループから抜ける
        while(true)
        {
            //プレイヤーターン
            yield return new WaitForSeconds(1f);
            ChangeTurn(BattleStatus.PlayerTurnStart);

            //プレイヤーの攻撃
            yield return new WaitForSeconds(1f);
            ChangeTurn(BattleStatus.PlayerAttack);
            if (BattleWin(enemy)) break;

            //敵のターン
            yield return new WaitForSeconds(1f);
            ChangeTurn(BattleStatus.EnemyTurnStart);

            //敵の攻撃
            yield return new WaitForSeconds(1f);
            ChangeTurn(BattleStatus.EnemyAttack);
            if (BattleLose(player))
            {
                _lose = true;
                break;
            }
        }

        yield return new WaitForSeconds(1f);

        //敵のHPが0だったら勝利
        if (!_lose)
        {
            Debug.Log("あなたは勝ちました");
        }
        //プレイヤーのHPが0だったら負け
        else
        {
            Debug.Log("あなたは負けました");
        }
    }

いきなり長いコードで驚きだと思いますが、一つ一つ解説していきます。
まずWhile文の中ですね、見ての通りなんですが、
それぞれのターンを1秒ごとに切り替えています。
プレイヤーの攻撃の後では、エネミーのHP0なのかを判定し、そうであるならループを抜けるようにしています。
続いて敵の攻撃の後にプレイヤーのHPが0なのかを判定し、そうであるなら負けフラグをTrueにしてループを抜けています。
プレイヤーも敵もHPが0でなければ、プレイヤーのターンに戻るって感じになります。ここの『BattleWin()』や『BattleLose()』の関数は後ほど説明します。
次に、ループが抜けた後の処理を説明していきます。
負けフラグが立っているなら、負けイベント。立っていないなら、勝利イベントを出しています。

勝利か負けかを判定

   //勝利関数
    bool BattleWin(CharacterStatus enemy)
    {
        if (enemy.hp <= 0)
        {
            enemy.hp = 0;
            return true;
        }
        else return false;
    }

    //負け関数
    bool BattleLose(CharacterStatus player)
    {
        if (player.hp <= 0)
        {
            player.hp = 0;
            return true;
        }
        else return false;
    }

ここでは引数に設定されたキャラクターのHPが0以下だったらの処理を書いています。勝利関数では敵のHPが0以下だったらHPを0にしてTrueを返す。
それ以外だったらFalseを返すにしています。
負け関数も同じで、引数がプレイヤーになっているところですね。

実際にプレイしてみよう!

これにて、ターン制システムの完成です!
実際にプレイを押してテストしてみましょう!
ログに結果が出てると思います。あとは自分でプレイヤーや敵のステータスを変えてみたり、
プレイヤーや敵はScriptableObjectを使うと管理が楽になったりするので修正したり、このバトルをUIに反映したらもっとゲームらしくなりますね!
もし機会があったら私もやってみたいと思います。
それではみなさんお疲れ様でした!


いいなと思ったら応援しよう!