Unity アークナイツライクなタワーディフェンスゲームを作りたい③ キャラクターのステータス実装

こんばんは。鈴木羽那が怖いです。

前回に引き続き、キャラクターの実装を進めます。
今回は、キャラクターが持つステータスの実装を行います。


今回の目標:キャラクターのステータスを設定する。

一般的なRPGに登場するキャラクターが持つ「攻撃力」「防御力」のようなステータスを、アークナイツに登場するキャラクターも保持しています。
添付画像の左側の部分が味方オペレーターのステータスです。

味方オペレーターのステータス

また、味方と同様に敵もステータスを保持しています。

敵のステータス

敵・味方で共通で保持しているステータスは下記になります。

・名前
・HP
・攻撃力
・防御力
・術耐性

他、味方のみが持つ以下のステータスがあります。

・部隊:先鋒、前衛等。所属部隊によって、戦闘での役割が異なります。
・配置コスト:戦場への配置に必要なコスト
・再配置時間:戦場から撤退した後再配置するまでのクールタイム
・ブロック可能数:敵をブロックできる数

細かくみていくと、もっとあるのですが、一旦ここまででまずは味方のステータスを実装してみます。

やったこと①クラス構造の定義

オペレータのステータスを定義するために、まずはクラス構造を定義していきます。

先述の通り、アークナイツに登場する味方・敵は一部共通のステータスを持ちます。また、キャラクターという点では味方・敵も根本では同じもので、攻撃した時のダメージ計算方法や、HPが0になったら死亡という点は同じです。

この辺を加味して、下記のような構造でクラスの実装を進めました。

クラス図もどき

クラス図と呼んではいけないお粗末なものですが、構造としては

キャラクタークラス:
全てのキャラクターに共通のステータス、動作を定義する

オペレータークラス:
キャラクタークラスを継承しつつ、味方のみに固有のステータス、スキル発動等の動作を定義する

敵クラス:
オペレータークラスとおんなじ感じ

という感じです。

やったこと② クラス実装

やったこと①に則って、素直にクラスを作りました。
まず以下はキャラクタークラスのコードです。

using UnityEngine;
using System;
using ArkKnightsLike.Configuration;

namespace ArkKnightsLike.CharacterModel
{
	/// <summary>
	/// 敵・味方含め全てのキャラクターのベースとなるクラス
	/// </summary>
	public class Character : MonoBehaviour
	{
		/// <summary>
		/// キャラクターを一意に定義するためのキー
		/// </summary>
		public int ID;

		/// <summary>
		/// 名前
		/// </summary>
		public string Name;

		/// <summary>
		/// HP
		/// </summary>
		public float HP;

		/// <summary>
		/// 攻撃力
		/// </summary>
		public float AttackPoint;

		/// <summary>
		/// 防御力
		/// </summary>
		public float DefencePoint;

		/// <summary>
		/// 術耐性
		/// </summary>
		public float MagicRegistance;

		/// <summary>
		/// 物理攻撃か術攻撃か
		/// </summary>
		public AttackType AttackType;
    }
}

ステータスの部分は、ほぼクラス図通りの内容です。

追加したものとしては"AttackType"のステータスです。
攻撃時のダメージ算出に、物理攻撃か術攻撃か等の定義が必要だったので、そのキャラクターの攻撃が物理か術かを定義するメンバを追加しました。

ステータス以外にも、攻撃・被弾時の動作も、同クラス内で実装しました。

     /// <summary>
		/// 攻撃力の分だけ相手を攻撃
		/// </summary>
		public AttackResult Attack()
		{
			return new AttackResult(this.AttackType, this.AttackPoint);
		}

		/// <summary>
		/// 攻撃を受けた時の動作
		/// </summary>
		public void TakeDamage(AttackResult attackResult)
		{
			this.HP -= CalcurateDamage(attackResult);

			//HPが0以下になったら死ぬ
			if (this.HP <= 0)
			{
				Die();
			}
		}

		/// <summary>
		/// 死ぬ時の動作
		/// </summary>
		public virtual void Die(){ }

		/// <summary>
		/// ダメージの計算を行う
		/// </summary>
		/// <param name="attackResult"></param>
		/// <returns></returns>
		private float CalcurateDamage(AttackResult attackResult)
		{
			var damageResult = 0f;
			switch (attackResult.AttackType)
			{
				case AttackType.Physical:
					//物理ダメージ - 防御力の差分を食らう
					damageResult = attackResult.Damage - this.DefencePoint;
					break;

				case AttackType.Magical:
					//術ダメージ - 術耐性の差分を食らう
					damageResult = attackResult.Damage - this.MagicRegistance;
					break;

				case AttackType.Absolute:
					//耐性によらず受けたダメージをそのまま食らう
					damageResult = attackResult.Damage;
					break;
			}

			//もしダメージが最低保証ダメージ以下なら、最低保証ダメージ5を食らう
			if (damageResult <= SystemCode.MinimumDamage)
			{
				damageResult = SystemCode.MinimumDamage;
			}

			return damageResult;
		}

攻撃した際は、相手に対して自身の攻撃力分の数値と、その攻撃が物理攻撃か、術攻撃かの種別を渡します。

それを受け取った際に、受け取ったキャラクターの防御力や術耐性を加味してどのくらいHPが減るかを、キャラクター側が算出するようになっています。

ダメージ計算の部分の実装はなんかそれっぽくて楽しかったです。

一方でオペレータのクラスの実装はこんな感じです。

using System;
using System.Xml;

namespace ArkKnightsLike.CharacterModel
{
	public class Operator : Character
	{
		/// <summary>
		/// レベル
		/// </summary>
		public int Level;

		/// <summary>
		/// 役割
		/// </summary>
		public RollType Roll;

		/// <summary>
		/// 配置コスト
		/// </summary>
		public int Cost;

		/// <summary>
		/// ブロック可能数
		/// </summary>
		public int BlockCount;

		/// <summary>
		/// 近距離か遠距離か
		/// </summary>
		public RangeType RangeType;

		/// <summary>
		/// スキル発動時の動作
		/// </summary>
		public void ActivateSkill()
		{

		}
	}
}

キャラクタークラスを継承しつつ、オペレータ特有のステータス・動作を定義しました。

スキル発動した際の動作は一旦空です。
オペレータ毎にあまりにも動作が異なるので、どう実装するか今のところ見当がついていません。今後の課題です。

やったこと③ prefabにアタッチ

先述までで作ったクラスを、実際にUnity上でPrefabにアタッチします。
アタッチのしかたは、ドラッグアンドドロップだけなので簡単です。

アタッチすると、こんな感じで定義したステータスがInspectorウィンドウに表示されます。

市川雛菜のステータス

これで市川雛菜の攻撃力やHPを自由に操作できるようになりました。

余談① Unityがプロパティを認識しない

アタッチした後、クラスで定義したステータスがInspectorに表示されずちょっと沼っていました。

原因は、最初アタッチするステータスをメンバ変数ではなくプロパティとして書いていたためです。

///これだとダメみたい
public int Level { get; set; }

上記のように自動プロパティとして定義してしまうとUnity上では認識しなくなってしまうみたいです。

これに関しては色々やりようがあるみたいなので、今後必要があれば改良していきます。

余談② enum型のフィールド

オペレータクラスの「部隊」について、部隊を列挙するenumを作成してそれを型とするメンバ変数を定義することで実装していました。

    /// <summary>
    /// 役割
    /// </summary>
    public RollType Roll;

   public enum RollType
    {
        /// <summary>
        /// 先鋒
        /// </summary>
        Vangurad = 0,

        /// <summary>
        /// 前衛
        /// </summary>
        Guard = 1,

        /// <summary>
        /// 重装
        /// </summary>
        Defencer = 2,

        /// <summary>
        /// 特殊
        /// </summary>
        Specialist = 3,

        /// <summary>
        /// 狙撃
        /// </summary>
        Sniper = 4,

        /// <summary>
        /// 術師
        /// </summary>
        Caster = 5,

        /// <summary>
        /// 医療
        /// </summary>
        Medic = 6,

        /// <summary>
        /// 補助
        /// </summary>
        Supporter = 7
    }

特に意図していたわけではないのですが、このフィールドをUnityのInspectorで見てみると、コンボボックスから定義した列挙子を選択することができるのを知りました。

部隊の選択肢

この辺はなんか気が利いててええやんと思いました。

結び

クラスの定義ができたので、次はデータの読み出しを実装したいです。
今は市川雛菜のステータス等をUnity 上で直入力することでしか設定できません。

しかし後々他のキャラクターやレベルアップ等の機能を実装するにあたり、そういうデータをデータベース上で管理する必要が出てくると思うので、そこら辺をやっていきたいと思います。

この記事が気に入ったらサポートをしてみませんか?