【Unity基礎9】CSVファイルから情報を読み取りゲーム画面へ反映させる方法

Unity基礎

ゲームではエネミーのHP・攻撃力といった情報をはじめ、共通の項目に対する多くの情報を事前に設定しておく必要がある場合があります。その際、これらのデータを表形式でまとめておき、それをUnityで別途読み込めるとデータの編集や管理が非常に楽です。そこで今回はスクリプトを使って表作成アプリケーションで作ったCSVファイルから情報を読み取り、さらにそれをゲーム画面へ反映させる方法について説明したいと思います。

準備

 

イメージ

今回も前回と同様、エネミーとエンカウントした時、指定したエネミーを表示させる処理を想定します。具体的にはにエネミーの名前・HPおよび画像の保管先をまとめたCSVファイルから情報を読み取り、下図のように、エネミーに対応するボタンボタンを押した時にそのボタンに対応するエネミーの画像と情報を表示させることとします。

各オブジェクトの配置

イメージが掴めたところで、実際に各オブジェクトを配置してみましょう。詳細は省略しますが、私は以下のようにボタン・イメージおよびテキストを配置しました。

上図におけるオブジェクトの名前と役割は以下の通りです。

各オブジェクトの名前と役割
EnemyNameText:ボタンが押された時にそれに対応するエネミー名を表示させる
HpText:ボタンが押された時にそれに対応するエネミーのHPを表示させる
EnemyImage:ボタンが押された時にそれに対応する画像を表示させる
SlimeButton:スライムを表示させるためのボタン
GoblinButton:ゴブリンを表示させるためのボタン
DragonButton:ドラゴンを表示させるためのボタン

 

CSVファイルから情報を読み取りゲーム画面へ反映させる方法

 

必要な準備が整ったので、続いてスクリプトを使ってCSVファイルから情報を読み取りゲーム画面へ反映させる方法について説明していきます。

STEP1:エネミー画像を用意する

先ずは表示させるエネミーの画像を用意します。今回はUnity基礎8と同様、下記サイト様の無料素材を使用させていただきます。

素材提供元:ぴぽや倉庫様
URL:https://pipoya.net/sozai/
使用させて頂いた素材:ポップモンスター46セットURL:https://pipoya.net/sozai/assets/enemyillust/enemy-image/

素材がダウンロードできたら、下記Unity基礎8 STEP1と同様にプロジェクトウィンドウのAssetsフォルダ直下にResourcesフォルダを作り、さらにその直下にImageフォルダを作って画像をドラッグ&ドロップします。

【Unity基礎8】スクリプトを使って特定の画像を表示させる方法
今回はゲームでのエネミーエンカウントを想定し、スクリプトから特定のタイミングで画像を表示させる方法について説明します。

STEP2:エネミーに関する情報をまとめたCSVファイルを作成する

画像の用意ができたら次にエネミーに関する情報をまとめたCSVファイルを作成します。私の場合はNumbersを使っているのでその画面を例にすると下記のような形となります。

ここで表中の各情報は下記のとおりです

表内情報の意味
EnemyID:重複しないエネミー固有のID
EnemyName:各IDに対応するエネミーの名称
Hp:各IDに対応するエネミーの
HPImageAddress:各IDに対応するエネミーの画像保管先

 

ImageAddressに関してはUnity基礎8 STEP6にも記載したように、拡張子をつけないなどの注意が必要です。また、アドレスのコピー方法についてもUnity基礎8に記載していますので、必要に応じて参照してください。
CSVファイル自体の作り方等は、ExcelやNumbersなど使用するアプリケーションにより異なると思いますので、ここでは説明を省略させていただきます。

STEP3:作成したCSVファイルを導入する

エネミーの情報をまとめたCSVファイルが作成できたらそれを作成中のプロジェクトへ導入します。具体的には下図のようにAssets直下のResourcesフォルダ内にCSVという名称でフォルダを作成し、そこに作成したCSVファイルをD&Dします。

STEP4:CSVファイルの情報を読み込むためのスクリプト作成する

次にSTEP3で導入したCSVファイルに記述された情報を読み込むスクリプト作成します。今回は下図のようにEnemyInformationという名前でスクリプトを作成しました。

STEP5:CSVファイルの情報を読み込む機能を実装

スクリプトを作成したらそれを編集してCSVファイルの情報を読み込む機能(メソッド)を実装します。

スクリプトの内容

先にスクリプトの内容を示すと下記の通りです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;//情報を読み込むStringReaderを使用するために導入
//エネミーの基礎情報をCSVから読み込んで、変数に格納する
public  class EnemyInformation//MonoBehaviourは継承しない
{
    static TextAsset csvFile;//CSVファイルを変数として扱うために宣言
    static List<string[]> enemyData = new List<string[]>();//CSVファイルの中身を入れる配列を定義。全てのデータが文字列形式で格納される
    //変数名[i]がエネミーIDがiの情報をそれぞれ示す
    public int[] enemyID = new int[100];//エネミーのID
    public string[] enemyName = new string[100];//エネミーの名前
    public int[] hp = new int[100];//エネミーのHP
    public string[] imageAddress = new string[100];//エネミーの画像イメージのアドレス
    //指定したアドレスに保管されているCSVファイルから情報を読み取り、enemyDataに情報を文字列として格納するメソッド。
    //enemyData[i][j]はCSVファイルのi行、j列目のデータを表す。但し先頭行(タイトル部分)は0行目と考えるものとする。
    static void CsvReader()
    {
        csvFile = Resources.Load("CSV/Basis9CSV") as TextAsset;//指定したファイルをTextAssetとして読み込み(ファイル名の.csvは不要なことに注意) 最初の行(タイトル部分)も読み込まれるのでそこは使用しない
        StringReader reader = new StringReader(csvFile.text);//
        while (reader.Peek() != -1)//最後まで読み込むと-1になる関数
        {
            string line = reader.ReadLine();//一行ずつ読み込み
            enemyData.Add(line.Split(','));//,区切りでリストに追加していく
        }
    }
    //enemyDataに一度CSVファイルのデータを読み込んだら他のプログラムから扱いやすいよう定義したenemyID等の変数にデータを格納する
    public void Init()
    {
        CsvReader();//enemyDataへ情報を一時格納
        //各変数へデータを格納
        for (int i = 1; i < enemyData.Count; i++)//エネミーIDが記述された最後まで読み込み。一行目はタイトルなのでi=0はデータとして扱わない
        {
            enemyID[i] = int.Parse(enemyData[i][0]);//string型からint型へ変換
            enemyName[i] = enemyData[i][1];
            hp[i] = int.Parse(enemyData[i][2]);
            imageAddress[i] = enemyData[i][3];
        }
    }
}
スクリプトの解説

上記スクリプトでは大きく分けて以下の2つの機能で成り立っています。

①CSVファイルから情報を読み取り、enemyDataという文字列型配列にその情報を格納する機能。(CsvReaderメソッド)

②enemyDataに格納された情報を、他のプログラムから扱いやすいよう別途定義したenemyID等の変数にデータを格納する機能。(Initメソッド)

先ずは①に関連する部分から概要を説明すると以下の通りです。

4行目: ファイルから情報を読み込むStringReaderを使用するために導入
8行目:プログラム内でCSVファイルを変数として扱うためにcsvFileという変数を宣言
9行目:CSVファイルの中身(情報)を入れる配列enemyDataを定義。全てのデータが文字列形式で格納される
15行目~26行目:指定したアドレスに保管されているCSVファイルから情報を読み取り、enemyDataに情報を文字列として格納するメソッド。enemyData[i][j]はCSVファイルのi行、j列目のデータを表す。但し先頭行(タイトル部分)は0行目と考えるものとする。
19行目:指定したファイルをTextAssetとして読み込み(ファイル名の.csvは不要なことに注意)
20行目~25行目:CSVファイルの内容を,区切りで読み込み、enemyDataに追加する処理(CSVファイルを読み込むときはenemyDataの部分以外は決まり文句と考えていい)

 

次に②に関連する部分の概要を説明すると以下の通りです。

10行目~14行目:エネミーの各種情報を格納する変数を配列で定義([100]は扱うデータの数に合わせて変更する)
27行目~39行目:CsvReaderメソッドで各情報を格納したenemyDataを10行目~14行目で定義したenemyID等の変数に再格納する処理
34行目:enemyDataの0列目のデータをint型に変換した上でenemyIDに格納する処理*0列目はCSVファイルの最初の列(EnemyID)であるため、行・列の数え方に注意が必要
35行目:enemyDataの1列目のデータをenemyNameに格納する処理
36行目:enemyDataの2列目のデータをint型に変換した上でhpに格納する処理37行目:enemyDataの3列目のデータをimageAddressに格納する処理

 

STEP6:各種情報の表示処理を実装するためのスクリプト作成する

次に各種情報の表示処理を実装するためのスクリプト作成します。今回は下図のようにButtonControllerという名前でスクリプトを作成しました。

STEP6~STEP9はUnity基礎8と重複する部分が多いため説明を簡略化させていただきます。必要に応じてUnity基礎8をご参照ください。

STEP7:操作対象とするオブジェクトをスクリプトに認識させる

続いて操作対象とするオブジェクトを先ほど作成したスクリプトに認識させます。ここで言う操作対象とは今回の例ではエネミーの画像を表示するEnemyImageオブジェクト、エネミー名前を表示するEnemyNameTextオブジェクトおよびHPを表示するHpTextオブジェクトです。

実際のスクリプトを示すと下記となります。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;//Image型を扱うために導入
public class ButtonController : MonoBehaviour
{
    [SerializeField] Text EnemyNameText = null;//エネミーの名前を表示させるTextオブジェクトとの連携のために導入
    [SerializeField] Text HpText = null;//エネミーのHPを表示させるTextオブジェクトとの連携のために導入
    [SerializeField] Image EnemyImage = null;//エネミー画像を表示させるImageオブジェクトとの連携のために導入
}

上記スクリプトに関しての説明は下記の通りです。

4行目:Image型,Text型を使用するために導入が必要。
(Image,Textオブジェクトを操作するために必要)
7~9行目:ButtonControllerクラスに、各情報を表示させるImageオブジェクト,Textオブジェクトを認識させるための構文。

 

STEP8:スクリプトをオブジェクトへアタッチする

上記STEP7で記述したスクリプトではまだ処理としては未完成ですが、説明のため、先ずは作成したスクリプトをボタンオブジェクトにアタッチします。今回のオブジェクトにアタッチするButtonControllerスクリプトは3つのボタンそれぞれがクリックされた時の操作をまとめて記述したいので、アタッチ先のオブジェクトとしてからのオブジェクトを作成し、その名前をButtonControllerObjectとしました。そしてこのButtonControllerObjectにButtonControllerスクリプトをアタッチします。(下図参照)

STEP9:スクリプトに操作したいオブジェクトを認識させる

STEP8でButtonControllerObjectにスクリプトをアタッチすると下図のように、インスペクターウィンドウ上でEnemyImage等の箱ができます。そこに表示させたいImage,Textブジェクトをドラッグ&ドロップします。

STEP10:スクリプトを編集し、表示処理を実装する

STEP9の作業により操作対象となるオブジェクトをスクリプトが認識できたので、続いて実際に操作したい内容をスクリプトに記述します。今回は下記のような内容となります。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;//Image型を扱うために導入
public class ButtonController : MonoBehaviour
{
    [SerializeField] Text EnemyNameText = null;//エネミーの名前を表示させるTextオブジェクトとの連携のために導入
    [SerializeField] Text HpText = null;//エネミーのHPを表示させるTextオブジェクトとの連携のために導入
    [SerializeField] Image EnemyImage = null;//エネミー画像を表示させるImageオブジェクトとの連携のために導入
    private EnemyInformation enemyInfo;//CSVを読み込むEnemyInformationクラスを扱うために宣言
    //ゲーム起動時の動作
    private void Start()
    {
        enemyInfo = new EnemyInformation();//EnemyInformationクラスの実体としてenemyInfo生成
        enemyInfo.Init();//CSVデータの読み込みと変数への格納処理
    }
    //「スライム」ボタンが押された時の動作。Imageオブジェクトにスライムの画像を表示させるとともに、Textオブジェクトに名前とHPを表示させる
    public void ClickSlimeButton()
    {
        EnemyNameText.text = enemyInfo.enemyName[1];//CSVファイル内でenemyID=1で表されるエネミーの名前ををTextオブジェクトに表示させる
        HpText.text = "HP:" + enemyInfo.hp[1].ToString();//CSVファイル内でenemyID=1で表されるエネミーのHPをTextオブジェクトに表示させる(int型から文字列型に変換するために.ToString()を使っている点に注意。
        //CSVファイル内でenemyID=1で表されるエネミーの画像保管先(enemyInfo.imageAddress[1])で表されるファイルを、変数EnemyImageと結び付けられたオブジェクトに表示させる処理。.pngなどの拡張子は不要
        EnemyImage.sprite = Resources.Load<Sprite>(enemyInfo.imageAddress[1]);
    }
    //「ゴブリン」ボタンが押された時の動作。Imageオブジェクトにゴブリンの画像を表示させるとともに、Textオブジェクトに名前とHPを表示させる
    public void ClickGoblinButton()
    {
        EnemyNameText.text = enemyInfo.enemyName[2];//CSVファイル内でenemyID=2で表されるエネミーの名前ををTextオブジェクトに表示させる
        HpText.text = "HP:" + enemyInfo.hp[2].ToString();//CSVファイル内でenemyID=2で表されるエネミーのHPをTextオブジェクトに表示させる(int型から文字列型に変換するために.ToString()を使っている点に注意。
        //CSVファイル内でenemyID=2で表されるエネミーの画像保管先(enemyInfo.imageAddress[2])で表されるファイルを、変数EnemyImageと結び付けられたオブジェクトに表示させる処理。.pngなどの拡張子は不要
        EnemyImage.sprite = Resources.Load<Sprite>(enemyInfo.imageAddress[2]);
    }
    //「ドラゴン」ボタンが押された時の動作。Imageオブジェクトにドラゴンの画像を表示させるとともに、Textオブジェクトに名前とHPを表示させる
    public void ClickDragonButton()
    {
        EnemyNameText.text = enemyInfo.enemyName[3];//CSVファイル内でenemyID=3で表されるエネミーの名前ををTextオブジェクトに表示させる
        HpText.text = "HP:" + enemyInfo.hp[3].ToString();//CSVファイル内でenemyID=3で表されるエネミーのHPをTextオブジェクトに表示させる(int型から文字列型に変換するために.ToString()を使っている点に注意。
        //CSVファイル内でenemyID=3で表されるエネミーの画像保管先(enemyInfo.imageAddress[3])で表されるファイルを、変数EnemyImageと結び付けられたオブジェクトに表示させる処理。.pngなどの拡張子は不要
        EnemyImage.sprite = Resources.Load<Sprite>(enemyInfo.imageAddress[]);
    }
}

上記スクリプトに関しての説明は下記の通りです。ただし、4,7~9行目は前述と変わりありませんので、説明は省略しています。また、25行目~40行目も17行目~24行目とほぼ同様の内容ですのでそちらについても省略します。

10行目:CSVを読み込むEnemyInformationクラスの実体を扱うために宣言
11行目~16行目:ゲーム起動時に呼び出されるメソッド14行目:STEP5で作成したEnemyInformationクラスを使用するため、実体としてenemyInfoを生成
15行目:EnemyInformationクラスのInit()メソッドを呼び出し、CSVファイルの情報をenemyInfoに格納
17行目~24行目:「スライム」ボタンが押された時の動作
20行目:CSVファイル内でenemyID=1で表されるエネミーの名前をTextオブジェクトに表示
21行目:CSVファイル内でenemyID=1で表されるエネミーのHPをTextオブジェクトに表示させる(int型から文字列型に変換するために.ToString()を使っている点に注意)
23行目:CSVファイル内でenemyID=1で表されるエネミーの画像保管先(enemyInfo.imageAddress[1])で表されるファイルを、変数EnemyImageと結び付けられたオブジェクトに表示させる処理。.pngなどの拡張子は不要

 

実際にゲームを制作する場合、私はSTEP7と一緒にSTEP10を行うことが多いです。今回は説明をわかりやすくする目的であえてステップを分けましたが、実際のゲーム制作の際は皆さんもお好みに合わせてください。

STEP11:ボタンオブジェクトのクリック時動作を設定する

最後にボタンクリック時の動作をボタンオブジェクトに認識させます。説明の重複を避けるため、ここでは「スライム」ボタンをクリックした時の動作設定のみ説明します。設定を行うには具体的には下図のように「スライム」ボタンオブジェクトのOnClick()に先に実装したClickSlimeButtonメソッドを設定します。他のボタンについても同様です。

ボタンクリック時の動作実装方法については下記「Unity基礎3」の「6.ボタンクリック時の機能実装」で詳しく説明していますので、必要に応じてご参照ください。

機能テスト

 

ここまでできたらPlayボタンを押してゲームを起動させ、処理が想定どおりに行われているか確認しましょう。実際に起動させてみた結果、下図のようにクリックしたボタンに応じて表示される画像やHP、名前が変更されました。

今回はCSVファイルから情報を読み取りゲーム画面へ反映させる方法について説明しましたがいかがだったでしょうか。今回説明したエネミーの表示のみならず、アイテムやキャラクター、ダンジョンの情報などCSVファイルで管理した方が楽な情報は数多くあると思います。皆さんのゲーム制作でも参考にしていただければ幸いです。

以上。

コメント

  1. unity初心者 より:

    記事の方、参考にさせて貰っております。
    ありがとうございます。
    ところで、CSVを読み込むさい、改行したい場合はどうすればよいでしょうか。
    CSVの文字列に改行をさせる”\n”を入れ込んでみましたが、改行されませんでした。
    ご教授いただければ幸いです。
    よろしくお願いいたします。

    • 藤野 藤野 より:

      記事を読んでいただき、ありがとうございます。

      お問い合わせいただいた件ですが、質問者様の意図としては
      例えばCSVファイルにファミリーネームとファーストネームを別の列に格納しておき、

      ファーストネーム(改行)
      ファミリーネーム

      という形で格納したいという理解でよろしいでしょうか?

      上記の場合、”\n”ではなくSystem.Environment.NewLineとするとうまくいくと思います。

      上述の例でのプログラムを下に示しますので、参考にしていただければ幸いです。

      最下行が1番のポイントとなる部分です。

      ////////////////////////////////////////////////////////////////////////////////////////

      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      using System.IO;//情報を読み込むStringReaderを使用するために導入
      //エネミーの基礎情報をCSVから読み込んで、変数に格納する
      public class LineTest//MonoBehaviourは継承しない
      {
      static TextAsset csvFile;//CSVファイルを変数として扱うために宣言
      static List enemyData = new List();//CSVファイルの中身を入れる配列を定義。全てのデータが文字列形式で格納される
      //変数名[i]がエネミーIDがiの情報をそれぞれ示す
      public int[] enemyID = new int[100];//エネミーのID
      public string[] FirstName = new string[100];//エネミーのファーストネーム
      public string[] FamilyName = new string[100];//エネミーのファミリーネーム
      public string[] Name = new string[100];//エネミーの名前

      //指定したアドレスに保管されているCSVファイルから情報を読み取り、enemyDataに情報を文字列として格納するメソッド。
      //enemyData[i][j]はCSVファイルのi行、j列目のデータを表す。但し先頭行(タイトル部分)は0行目と考えるものとする。
      static void CsvReader()
      {
      csvFile = Resources.Load(“CSV/NTEST”) as TextAsset;//指定したファイルをTextAssetとして読み込み(ファイル名の.csvは不要なことに注意) 最初の行(タイトル部分)も読み込まれるのでそこは使用しない
      StringReader reader = new StringReader(csvFile.text);//
      while (reader.Peek() != -1)//最後まで読み込むと-1になる関数
      {
      string line = reader.ReadLine();//一行ずつ読み込み
      enemyData.Add(line.Split(‘,’));//,区切りでリストに追加していく
      }
      }
      //enemyDataに一度CSVファイルのデータを読み込んだら他のプログラムから扱いやすいよう定義したenemyID等の変数にデータを格納する
      public void Init()
      {
      CsvReader();//enemyDataへ情報を一時格納
      //各変数へデータを格納
      for (int i = 1; i < enemyData.Count; i++)//エネミーIDが記述された最後まで読み込み。一行目はタイトルなのでi=0はデータとして扱わない { enemyID[i] = int.Parse(enemyData[i][0]);//string型からint型へ変換 FirstName[i] = enemyData[i][1]; FamilyName[i] = enemyData[i][2]; Name[i] = FirstName[i] + System.Environment.NewLine + FamilyName[i]; } } }