読者です 読者をやめる 読者になる 読者になる

テラシュールブログ

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

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

Unity Excel アセット・ファイル管理 エディタ拡張・エディタ機能

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

以前同じ記事を書いたがよりよい方法があったので修正版をアップする事にした。

サンプルについてはこちら。

https://dl.dropboxusercontent.com/u/56297224/UnitySumple2/Excel%E9%80%A3%E6%90%BA/excel%20sample2.unitypackage


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

紹介用のソースを出そうと思ったが、githubが死んでるのでまた今度。


まず?についてだが、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に流しこむ部分。
以前Excel Data Reader - Read Excel files in .NETを紹介したが、それよりNPOIの方が.NET環境と親和性が高い(.NETとよく似ている)

NPOI 2.0 beta 1
http://npoi.codeplex.com/

まず上のページからNPOI 2.0をダウンロードし、dotnet3.5のファイルをEditorフォルダ以下に配置する。Unity は.net2.0を魔改造していて.NET 3.5系まで使用出来るので、この手のライブラリも使用出来る。

使い方については下のページが参考になった。

[Unity]NPOI2.0.1(beta 1)を使ってxlsとxlsxを読み込んでみた
http://caitsithware.sakura.ne.jp/wordpress/?p=108

まずExcelを読む。

IWorkbook book = new HSSFWorkbook (stream);	// xls
//IWorkbook book = new XSSFWorkbook(stream); // xlsx

後はシートを読んでセルを読んで流し込む。
book.GetSheetAt (シート番号)でシート情報を取得し、後は指定列の指定行の情報を取得する。

		ISheet sheet = book.GetSheetAt (0);
for (int i=1; i< sheet.LastRowNum; i++) {
// セル情報を取得
IRow row = sheet.GetRow (i);

ExcelData.Param p = new ExcelData.Param ();
p.skillName = row.GetCell (0).StringCellValue;
p.skillEffect = row.GetCell (1).StringCellValue;
p.damage = (int)row.GetCell (2).NumericCellValue;

data.list.Add (p);
}

ちなみにEditorフォルダに入れている理由は簡単で、Editor以外にあるとビルド時にアプリに含めてしまう。そのため、Editorでしか使わないDLLはEditorフォルダに入れるべき。

ちなみに、sheet.SheetNameでシート名が取得出来る。
上手く使えば、シート毎にステージ情報を別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へ流しこんでくれる。

計算結果も含めることが出来るので、かなり色々な事に使えると思う。


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