【Unity】スキル発動処理を考える。コスト・属性相性や全体攻撃も実装したい

【Unity】スキル発動処理を考える。コスト・属性相性や全体攻撃も実装したい

 RPGゲームで超重要な戦闘システムを考えます。複数の効果を持つスキルを簡単に追加できるようにメンテナンス性を追求しました。皆さんの考えもコメントで教えて頂ければ幸いです。当記事はコピペで実装できるものではなく、あくまで考え方を共有するモノです。

実装したいこと

 スキルを発動する関数を作成したい。

 CSVファイルにスキルの効果等を書き込むだけで、スキルを追加したり修正したい。

 CSVファイルからスキルの詳細を読み取って処理を行いたい。

 必要なコスト・属性・物理か魔法を判別して処理をする。

実装手順

スキルの効果を格納する構造体を作る

 構造体とは、複数の変数を1つのモノとして扱えるものです。

 スキルの名前や属性など必要な変数を全て詰め込みます。

 ですが、ここで1つ考えておきたいことがあります。

 スキルのコストと効果は必ず1種類だけにしますか?

 個人的には複数の効果が合ってほしいです。

 攻撃と同時に回復したり、スキルの発動にMPとアイテムが必要なスキル等を実装してみたいですよね。

 ですので、先にスキルの効果だけを格納する構造体を作ります。

//スキルの効果等を格納する構造体
    public struct SKILL_TYPE
    {
        public int effect_type;
        public float effect_amount;
        public int target;
        public int cost_type;
        public float cost_amount;
    }
int effect_type効果の種類 0:効果なし
1:攻撃スキル
2:回復スキル
3:物理攻撃バフ
4:魔法攻撃バフ
5:物理防御バフ
6:魔法防御バフ
7:最大HPバフ
float effect_amount効果量整数
int targetターゲットの種類 0:誰も対象にしない
1:自分
2:味方1人
3:味方全員
4:敵1人
5:敵全体
6:ランダム
int cost_typeコストの種類 0:コスト無し
1:HPを消費
2:MPを消費
3アイテムを消費
float cost_amountコストの量 浮動小数点

スキルの構造体を作る

 こちらがメインの構造体になります。

 先ほどスキルの効果を格納する構造体を作りました。

 スキルの効果の数だけ、リストに追加させます。

//スキルを格納する構造体
    public struct SKILL
    {
        public int flag;
        public int id;
        public string name;
        public string info;
        public int attribute_type;
        public float interval;
        public int type_n;
        public List<SKILL_TYPE> skill_type;
    }
int flagスキルが解放されているかどうか0:未開放
1:解放
int idスキルのID整数
string nameスキルの名前文字列
string infoスキルの説明文字列
int attribute_typeスキルの属性 0:無属性
1:物理属性
2:魔力属性
3:火属性
4:水属性
5:土属性
6:風属性
7:光属性
8:闇属性
9:回復属性
float intervalスキルの発動間隔浮動小数点
int type_n効果の数整数
この数だけリストに追加する
List skill_type効果を格納するリスト前項で定義した構造体

CSVファイルにスキルの詳細を書き込む

 メモ帳を開き、”,”で区切りながら記述します。

 保存したファイルの拡張子を.txtから.csvに変更すれば完成です。

 おすすめの無料のCSVリーダーはこちらです。以下の画像のように整って表示されます。

 2行目以降を読み込むようにすれば、1行目に説明を書けるので管理が楽です。

 当記事と同じように実装するなら、以下のリストの順番に記述してください。

  1. 解放フラグ
  2. ID
  3. 名前
  4. 説明
  5. 属性
  6. 発動間隔
  7. 効果の数
  8. 効果の数だけセットで追加(効果の種類・対象・効果量・コストの種類・コストの量)

CSVファイルを読み込む関数を作る

 こちらで詳しく解説していきますので、よくわからない方は読んでみてください。

 前準備として、Resourseファルダを作ってCSVファイルを入れておきましょう。

 後は以下のコードでCSVファイルを読み込むことができます。

csvFile = Resources.Load("CSV/" + name) as TextAsset;

 後は読み込んだCSVファイルを”,”で区切りながら1行ずつ構造体に入れていきます。

//SKILL構造体のcsvファイルを読み込む
    public List<SKILL> SKILL_read_csv(string name)
    {
        //一時入力用で毎回初期化する構造体とリスト
        SKILL sk = new SKILL();
        List<SKILL> sk_list = new List<SKILL>();

        //CSVの読み込みに必要
        TextAsset csvFile;  // CSVファイル
        List<string[]> csvDatas = new List<string[]>(); // CSVの中身を入れるリスト
        int height = 0; // CSVの行数
        int i = 0;//debugループカウンタ

        /* Resouces/CSV下のCSV読み込み */
        csvFile = Resources.Load("CSV/" + name) as TextAsset;
        StringReader reader = new StringReader(csvFile.text);
        while (reader.Peek() > -1)
        {
            string line = reader.ReadLine();
            csvDatas.Add(line.Split(',')); // リストに入れる
            height++; // 行数加算
        }
        for (i = 1; i < height; i++)
        {
            Debug.Log("スキルを読み込んだ");
            SKILL_TYPE tmp = new SKILL_TYPE();
            sk.skill_type = new List<SKILL_TYPE>();
            sk.flag = Convert.ToInt32(csvDatas[i][0]);
            sk.id = Convert.ToInt32(csvDatas[i][1]);
            sk.name = csvDatas[i][2];
            sk.info = csvDatas[i][3];
            sk.attribute_type = Convert.ToInt32(csvDatas[i][4]);
            sk.interval = Convert.ToSingle(csvDatas[i][5]);
            //効果の種類数
            sk.type_n = Convert.ToInt32(csvDatas[i][6]);
            for (int j = 0; j < sk.type_n; j++)
            {
                int n = j * 5;
                tmp.effect_type = Convert.ToInt32(csvDatas[i][7 + n]);
                tmp.target = Convert.ToInt32(csvDatas[i][8 + n]);
                tmp.effect_amount = Convert.ToSingle(csvDatas[i][9 + n]);
                Debug.Log("tmp.effect_amount" + tmp.effect_amount);
                tmp.cost_type = Convert.ToInt32(csvDatas[i][10 + n]);
                tmp.cost_amount = Convert.ToSingle(csvDatas[i][11 + n]);
                sk.skill_type.Add(tmp);
            }
            Debug.Log(sk.skill_type[0].effect_amount);

            //戻り値のリストに加える
            sk_list.Add(sk);
        }
        return sk_list;
    }

 CSVファイルを要素で分解し、構造体に1つ1つ入れています。

 効果の数だけ追加でロードしているのもポイントですね。

 最後に格納したスキルの構造体リストを返します。

 後はStart内で使うと読み込んでくれます。

void Start(){
  skill = SKILL_read_csv("skill");
}

スキルの処理を作成

 少し複雑になりますので画像で簡単にイメージをお伝えします。

 今回のプログラムではスキル発動者の構造体FRIENDを使用しています。

 コストが足りているなら装備スキルを発動。

 足りていないのなら、デフォルトのコスト0スキルを発動といった形です。

 これは無くても大丈夫なのであくまで参考にしてくださいね。

//仲間を格納する構造体
    public struct FRIEND
    {
        public int flag;
        public int id;
        public string name;
        public string info;
        public int attribute_type;
        public float HP_now;
        //ステータスの基礎値
        public float HP;
        public float STR;
        public float INT;
        public float DEF;
        public float RES;
        //スキル
        public int skill_id;
        public int skill_id_eq;
    }

 スキルの処理は大まかにこのようになります。

//味方がスキルを実行
public static void do_skill(FRIEND fri)
{

//発動するスキル
SKILL sk = new SKILL();

//スキルを装備しているなら
if (fri.skill_id_eq != -1)
{
  //装備スキルに決定
  sk = skill[fri.skill_id_eq];
}else
{
  //デフォルトスキルに決定
  sk = skill[fri.skill_id];
}

//コストフラグ
bool can_lose = true;

//コストが足りているかチェック
for (int i = 0; i < sk.type_n; i++)
{
  //コストが足りていないならcan_loseをfalseに
  if (can_lose == true){(省略)}
  else{break;}
}

//コストが足りているなら
if(can_lose == true){
  //効果の数だけ実行
  for (int i = 0; i < sk.type_n; i++)
  {
    //コストを特定して減算
    switch (sk.skill_type[i].cost_type){(省略)}

    //対象を特定
    switch (sk.skill_type[i].target){(省略)}

    //処理を開始
    switch (sk.skill_type[i].effect_type){(省略)}
  }
}
}

 この筋道に従って必要な変数や処理作って行きます。

 スキルの発動処理を考える参考になればと幸いです。

 かなりメンドクサイので苦手な方はアセットに頼ることをお勧めします。