テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

Excelでデータを管理してUnity iOS/Androidで使うワークフローをもう少し詳しく。

Excelでデータ管理してUnity iOS/Androidに使うワークフローをもう少し詳く。


まず、このフローは4つの機能で構成される。
?.Excelファイルインポート時に反応する
?.データの形状に対応するScriptableObjectを生成する。既存の物があれば使用
?.Excelを解析し、2で取得したScriptableObjectに流しこむ
?.変更を確定する

紹介甩のソースコード全部はこちら。
https://gist.github.com/tsubaki/5900938


まず?についてだが、AssetPostprocessorOnPostprocessAllAssets使用する。
コンテンツパイプライン拡張の機能で、要するにUnityプロジェクトにファイルを追加した際何らかのアクションを行うクラス。
これをEditorフォルダ内に入れて動作させる。

これの第一引数が、インポートした全てのファイルパスを寄越すので、その中から取り出したいxlsファイルを選択する感じ。

ソースコードでは、以下の処理の部分


// ?.Excelファイルインポート時に反応する.
static void OnPostprocessAllAssets (
string[] importedAssets,
string[] deletedAssets,
string[] movedAssets,
string[] movedFromAssetPaths)
{
foreach (string asset in importedAssets) {

if (!filePath.Equals (asset))
continue;


次にScriptableObjectの生成について。
ScriptableObjectの概要については、以前記載したので割愛。要するにUnityの読みやすいデータ。
tsubakit1.hateblo.jp

ここでの処理も簡単で、ScriptableObjectを継承したFileDataクラスアセットを探して、なければ作る…といった感じ。注意すべきはエディタ内のフォルダ管理をSystem.IOではなくAssetDatabaseでやってる点。これはAssetを使う場合のお約束みたいな物なので、そうなのかー程度に覚えておけばOK。

毎回消して作らないのは、消して作るとエディタ内のアセットIDが生成前と生成後で異なってしまう為。要するにシーンやプレハブからの参照が切れてしまう。

			FileData data = (FileData)AssetDatabase.LoadAssetAtPath (exportPath, typeof(FileData));

if (data == null) {
data = ScriptableObject.CreateInstance ();
AssetDatabase.CreateAsset ((ScriptableObject)data, exportPath);
}

ちなみに個別のScriptableObjectを作らず「汎用クラスを作って管理すれば良いじゃん」みたいな考え方には反対。というか出来ない。

一つ目の理由が、List内のListのような伸縮自在の構造をUnityのシリアライズは許可出来ない点。そのため、Excelの縦x横yといった規模のデータ構造を再現出来ない。

もう一つの理由は、汎用データを作る場合Stringでデータを持つ事になると思うが、Stringはデータサイズが大きくなりやすい上、他のクラスへパースする際に時間がかかる為。

もし手間を省きたいなら、パースとデータ型を自動生成するような機能を用意する必要がある。

ということで、出力先・入力先が固定となるので、出力先ファイル・入力先ファイルが固定パスとして記述されてる。


そしてお待ちかねExcelに流しこむ部分・・・・・も先日紹介しているの割愛。
tsubakit1.hateblo.jp

ちなみにExcel.dllは可能な限りEditorに入れるべき。というのも、Editor以外にあるとビルド時にアプリに含めてしまう。そのため、Editorでしか使わないDLLはEditorフォルダに入れるべき。

			// ?.Excelを解析し、2で取得したScriptableObjectに流しこむ.

data.dataList.Clear ();

using (FileStream stream = File.Open (filePath, FileMode.Open, FileAccess.Read)) {
// excelを読む
IExcelDataReader excelReader = ExcelReaderFactory.CreateBinaryReader (stream);

// 最初のシートを取得(もう一度呼ぶと次のシートへ)
excelReader.NextResult ();

// 最初の行はタイトルなので飛ばす
excelReader.Read ();

// 行がなくなるまで読む
while (excelReader.Read()) {

FileData.Data d = new FileData.Data ();
d.version = excelReader.GetInt32 (1);
d.url = excelReader.GetString (2);

data.dataList.Add (d);
}

excelReader.Close ();
}

ちなみに、excelReader.nameでシート名が取得出来る。
上手く使えば、シート毎にステージ情報を別ScriptableObjectとして生成…なんてことも出来る。

下のゲームでは、その方法でゲームの難易度(Hard/Easy)を管理していた。

トロッ娘
http://www.unitygames.jp/game/ug7000556


最後に変更を確定する。

これもおまじないみたいなもので、一度AssetDatabaseからScriptableObjectを取得し、EditorUtility.SetDirtyで確定する。これをしないとエディタ終了時に無かったことにされる。

			// ?.変更を確定する.

ScriptableObject obj = AssetDatabase.LoadAssetAtPath (
exportPath, typeof(ScriptableObject)) as ScriptableObject;
obj.hideFlags = HideFlags.NotEditable;
EditorUtility.SetDirty (obj);


これで、後は指定パスへファイルを配置すると、自動でUnityが読み込んでScriptableObjectへ流しこんでくれる。

ただ、試した所計算式があると駄目だったので、思ったより使えない感(´・ω・`)
データの管理や一覧の取得等には使えるかもしれない。

計算結果を使いたい場合は、xlsをXMLか何かとして出力し、Excelを読んでる部分をXMLを読む部分に差し替えて読むと良さげ。
対策発見。明日また書く予定


まさかツィート回数が50回にもなるとは思わなんだ。
リツィートしてくれた人、ツィートしてくれた人ありがとうございます m(_ _ )m