白黒羊

Unity Asset「CSV Serialize」を使う

「CSV Serialize」という無料アセットをダウンロードしたので使用感を書いておきます。

このアセットは、csvファイルをScriptableObjectに変換してくれるアセットです。
ScriptableObjectはInspectorでデータが入力できるので扱いやすい一方で、保存するデータ種別を変更するとデータがなくなってしまうこともあります。
このアセットを使ってExcelやGoogle Spreadsheetから書き出したcsvファイルをScriptableObjectに変換することでデータとしての使いやすさと保存性を両立できそうです。

環境

  • Unity 2019.1.3.f1
  • CSV Serialize ver.1.0

導入

アセットストアからダウンロード/インポートするだけです。
私の環境では、インポートした瞬間に複数のWarningが出ました。

There are inconsistent line endings in the ‘Assets/CSVSerializer/Demo/.cs’ script. Some are Mac OS X (UNIX) and some are Windows.
This might lead to incorrect line numbers in stacktraces and compiler errors. Many text editors can fix this using Convert Line Endings menu commands.

改行コードが違うみたいです。
私は一度自環境で開いて保存して閉じたら解決しました。
それでもダメな場合は下の記事などを参考に、修正しておいた方が良さそうです。

ConstData must be instantiated using the ScriptableObject.CreateInstance method instead of new ConstData.
UnityEngine.ScriptableObject:.ctor()
ConstData:.ctor()
System.Activator:CreateInstance(Type)
CSVSerializer:CreateIdValue(Type, List`1, Int32, Int32) (at Assets/CSVSerializer/CSVSerializer.cs:121)
CSVSerializer:DeserializeIdValue(String, Int32, Int32) (at Assets/CSVSerializer/CSVSerializer.cs:24)
CSVImportExamplePostprocessor:OnPostprocessAllAssets(String[], String[], String[], String[]) (at Assets/CSVSerializer/Demo/CSVImportExample.cs:97)
UnityEditor.AssetPostprocessingInternal:PostprocessAllAssets(String[], String[], String[], String[], String[])

エラーダイアログの指摘箇所ではありませんが、「CSVImportExample.cs」の93行目、

gm = new ConstData();

gm = ScriptableObject.CreateInstance<ConstData>();

に書き換えました。
ScriptableObjectはnewで生成しないんですね。

【参考】
ScriptableObjects must be instantiated with ScriptableObject.CreateInstance instead of new · JetBrains/resharper-unity Wiki · GitHub

Assets\CSVSerializer\Demo\CSVImportExampleFromWeb.cs(30,22): warning CS0618: ‘UnityWebRequest.Send()’ is obsolete: ‘Use SendWebRequest. It returns a UnityWebRequestAsyncOperation which contains a reference to the WebRequest object.’

言われたとおりに「CSVImportExampleFromWeb.cs」の30行目の、

yield return www.Send();

yield return www.SendWebRequest();

に書き換えました。

概要

ここからは付属の「Readme.pdf」を参考に使い方を見ていきます。

はじめに、以下のようなデータのクラスを宣言しておきます。

[System.Serializable]
public class Sample
{
	public int year;
	public string make;
	public string model;
	public string description;
	public float price;
}

csvファイルからまず string としてデータを読み込みます。
これをDeserializeすることによってそれ以外のオブジェクトとしても扱えるようになります。

string text = "Year,Make,Model,Description,Price\r\n"
+ "1997,Ford,E350,\"ac, abs, moon\",3000.00\r\n"
+ "1999,Chevy,\"Venture \"\"Extended Edition\"\"\",\"\",4900.00";
Sample[] sample = CSVSerializer.Deserialize<Sample> (text);
csv_serialize_overview

なお、複数のシートには対応していないため、複数のシートに分けたデータを使いたい場合にはそれぞれのcsvファイルとして保存する必要があります。

サンプル

1-1 国のランキング

このようにデータを用意します。

public class RankingData : ScriptableObject
{
	public enum Country
	{
		gb=1,
		de=2,
		fi,
		be
	}
	[System.Serializable]
	public class Item
	{
		public int ranking;
		public string driver;
		public string constructor;
		public int score;
		public int podium;
		public Sprite icon ;
		public Country country;
		public string[] win ;
	}
	public Item[] m_Items;
}

ここでの変数の宣言順番はどのようにしてもデータの順番が変わることはありません。
コンマで区切られた文字列は配列として読まれます。

enum型はそのままで使えるのですが、それ以外の型を使いたい場合はCSVSerializer.csを編集する必要があります。
Sprite型が使えるようになっているのでそれを参考に自分の使いたい型も追加します。

#if UNITY_EDITOR
else if (fieldinfo.FieldType == typeof(UnityEngine.Sprite) )
{
	Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite> (value.ToString());
	fieldinfo.SetValue(v, sprite);
}
else if (fieldInfo.FieldType == typeof(Color))    // Color型を追加
{
        var hexColor = "#" + value;
        ColorUtility.TryParseHtmlString(hexColor, out var color);
        fieldInfo.SetValue(v, color);
}
#endif
csv_serialize_sample1-1_inspector
csv_serialize_sample1-1_demo_scene

CSVImportExample.cs」では、csvファイルをアセットファイルとして読み込んでいます。
もしcsvフォーマットが変更されたとき自動的に読み込まれますが、うまく読み込まれていないときは、「右クリック」>「Reimport」で再読み込みしてください。

1-2 ローカライズ

このようにローカライズ対応が簡単にできるようになります。

2 複数データを持つオブジェクト

データの定数を定めることができます。

csv_serialize_sample2_dataset
public class ConstData : ScriptableObject
{
	public int maxhp;
	public string type;
	public int[] intarray;
	public float[] floatarray;
	public string[] stringarray;
}
ConstData readdata = CSVSerializer.Deserialize IdValue <ConstData>(text);
csv_serialize_sample2_inspector

Google Spread Sheet からの読み込み

Google Spreadsheet からデータを読み込むこともできます。

https://docs.google.com/spreadsheets

新規作成して、これまでやってきたようにデータを作成します。

「ファイル」>「ウェブに公開」を選択し、作成したデータの入っているシートの名前を選び、データ形式を「csv」にします。
「ウェブに公開」からわかるようにこのデータは公開されますので隠しておきたい情報を扱うときはやめておきましょう。

csv_serialize_tip1_publish_to_the_web_ja
csv_serialize_tip1_settings_ja

「公開」ボタンを押して、リンクをコピーします。

その後の処理は「CSVImportExampleFromWeb.cs」を参考に書きましょう。

id,code,name,type,version
create_account,91,uuid,String,
,,type,Int,
login_account,93,authid,String,
,,authkey,Int,
,,ver,Int,1
ping,70,,,
pong,71,,,
purchase_complete,78,productid,String,
,,developerpayload,String,
,,hash_text,String,
,,signature,String,
,,purchasetoken,String,
List <string[]> row = CSVSerializer.ParseCSV(text) ;
List<Packet> packet = new List<Packet>();
for (int i = 1; i < row.Count;)
	{
	List<string[]> items = new List<string[]>();
	items.Add(row[0]); // id,code,name,type,version
	do
	{
		items.Add(row[i++]);
	} while (i < row.Count && row[i][0] == "");
	Packet p= new Packet();
	p.id = items[1][0];
	p.code = (int)Convert.ChangeType(items[1][1], typeof(int));
	if (items.Count == 2 && items[1].Length > 2 && items[1][2] == "")
	p.val = new Packet.Variable[0];
	else
	p.val = CSVSerializer.Deserialize<Packet.Variable>(items) ;
	packet.Add(packet);
}