主なコンテンツ

〜主なコンテンツ〜

1. Unityで製作したゲームと製作Tips
  1. 三月精チャレンジ(東方Project二次創作)
    1. 作り方
  2. 英語学習2D(オリジナルスマホアプリ)
2. UE4
3. ゲームアプリ見学
4. Bitbucket & SourceTreeでの一連の流れ
  1. 前半
  2. 後半
5. Tips
  1. UnityのTips
  5. SQL文のTips
  6. Final IK
  7. GearVR+Unity

2016年4月17日日曜日

NPOIを用いてExcelファイルのデータをScriptableObjectに書き込む(基礎編)

*参考先:過去の記事
xlsxは不可能になった模様(xlsなら可能)
*スクリプトの全容は最後に紹介

手順概要

  1. AssetPostprocessor.OnPostprocessAllAssetsを用いて、Excelファイルがインポートされた時に処理を行う(所謂エディタ拡張を行う)
  2. ファイルを開く(System.IO)
  3. 開いたファイルを読み込む(ExcelデータをC#で扱うためにNPOIが必要となる)
  4. データを元にScriptableObjectを作成(どのようなパラメーターを持つかは事前に作成しておく:参考
  5. ScriptableObjectをアセットとして保存

1. AssetPostprocessor.OnPostprocessAllAssetsについて

使える引数が4つあり、その意味は下記の通り。(公式リファレンスのソースを試してみると分かりやすい)
    1. インポートされたアセットのパス
    2. デリートされたアセットのパス
    3. 移動先のアセットのパス
    4. 移動元のアセットのパス
今回はインポートされたアセットのパスを利用する。

2. ファイルを開く

ここまでの処理は下図の通り。23行目でファイルを開き、開いたファイルのデータを元にIWorkbook型のオブジェクトを作成。このIWorkbook型を使ってC#でデータのやり取りをするためにNPOIが必要となる。


26行目のReadBookは自作のメソッド。処理を分りやすくするためにExcelファイル内での処理を別メソッドとして分けた。この処理は後述するとして、まずはNPOIの解説に移る。

3. NPOIについて

NPOIのDLLを入手(こちら

最新版は NPOI 2.2.0 だったのでこれを使用。DLしたフォルダの内部は以下の通り。

Unityは.NET3.5相当と言われているが、.NET4.0に対応したとの記事は見つからなかった。なので、今回はdotnet2のフォルダをプロジェクトのEditorフォルダにインポートした。NPOIはExcel→ScriptableObjectに変換する時に使うだけなので、Editorフォルダに入れておけばビルド時にゲームには同梱されずに済む。

名前空間について
  1. ファイルの読み書きをするので、System.IOが必要。
  2. IWorkBookを使うために、NPOI.SS.UserModelが必要
  3. xlsxの読み書きにはNPOI.XSSF.UserModelが必要 恐らく不可能になった
  4. 旧式ファイル形式(xls)の読み書きにはNPOI.HSSF.UserModelが必要

IWorkBookをXSSFで作成しようとしたところ、次のようなエラーが出た。

過去に動いたプログラムも動かなくなっていたので、Unity5.3になったことが影響していると思われる。HSSFなら回避可能。椿さんのプログラムもXSSFは削除しており、その削除理由が「Unity Test Toolsとの競合を避けるため」とある(参考)。このUnity Test ToolsはUnity5.3でUnit Tests RunnerとしてUnity組み込みになった参考)。恐らくこれによりXSSFがUnity本体と競合するようになったと思われる。xlsxは諦め、旧式のxlsでエクセルのファイルを作った方が良さそう。この時点であまり将来性のない技術になってしまったかもしれない。

4. データを元にScriptableObjectを作成

■ Step 1
まずは既に出力されているか確認する。ロードしてみてNULLが返ってくるようなら新規作成する。その後は、ファイルが既にあろうがなかろうがリストを破棄して、最新の状態に書き換える。WriteSOは自作メソッドであり、これについては後述する。


上図に出てきたCategoryDBとは、ScriptableObjectを継承した自作のクラス。作り方は前回の記事参照(前回は別なクラスを例にとっているがやっている事は同じ)。


備考
    1. このクラスはゲーム内で使用するためEditorには含める事はできないので注意
    2. 今回はメニューから作るわけではないので[CreateAssetMenu()]は不要

■ Step 2
Step1で目的のアセットファイルをロードもしくは新規作成した。次にこのファイルに対して書き込みを行う。書き込みの処理を行うWriteSOは、次のようなメソッドである。


Excelに関わる処理はNOPIで行う。このNPOIは、Java用に作られたPOIを元に作られている。なので使い方はPOI のAPIを参考にすることになる。

注意点
    1. シートも行列も数え始めは0である。例えばセルの1行目は、NOPIでは0行目である。
    2. 上記スクリプトのように列→行の順であり、この逆はできない

なお、今回使用したExcelファイルの中身は次の通りである。




■ Step 3
今回加えたいパラメーター群は、CategoryDBクラスの内部クラスであるCategoryDataである。なので、Step2のスクリプトの53行目でこのクラスを生成し、59~61行目でExcelのセルのセルの値を取得している。

こうして出来上がったCategoryDataを、Step1で用意したdataのリストに加えていく。リストに加えるにはAddを使う。


終わりに

以上の作業で、ExcelからScriptableObjectのオブジェクトを作成できる。xlsファイルをインポートすると指定したパスに、Excelの内容が反映されたファイルが自動生成される。



スクリプトの全容は下記の通り。

using UnityEngine;
using UnityEditor;
using System.IO;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
public class Importer : AssetPostprocessor {
static void OnPostprocessAllAssets (string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) {
foreach (string asset in importedAssets){
if(asset.EndsWith(".xls")){
Debug.Log (asset);
ReadXLS (asset);
}
}
}
static public void ReadXLS(string asset){
//.assetの出力先を用意
string filename = Path.GetFileNameWithoutExtension(asset);
string exportPath = "Assets/Resources/ScriptableObject/"+filename+".asset";
//ファイルを開く
using(FileStream stream = File.Open(asset, FileMode.Open, FileAccess.Read)){
//IWorkbookオブジェクトの作成
IWorkbook book = new HSSFWorkbook(stream);
ReadBook (exportPath, book);
}
//.assetの更新を確定
ScriptableObject scrObj = AssetDatabase.LoadAssetAtPath(exportPath, typeof(ScriptableObject)) as ScriptableObject;
EditorUtility.SetDirty(scrObj);
}
static public void ReadBook(string exportPath, IWorkbook book){
//.assetをloadしてみる
CategoryDB data = (CategoryDB)AssetDatabase.LoadAssetAtPath (exportPath, typeof(CategoryDB));
//.assetがまだ無ければcreateする
if (data == null) {
data = ScriptableObject.CreateInstance<CategoryDB> ();
AssetDatabase.CreateAsset ((ScriptableObject)data, exportPath);
}
//古いリストを破棄
data.list.Clear ();
//ScriptableObjectに記入
WriteSO(book, data);
}
static public void WriteSO(IWorkbook book, CategoryDB data){
//シートの指定
ISheet sheet = book.GetSheetAt (0);
Debug.Log (sheet.LastRowNum);
//各列を探索
for(int i=1; i<= sheet.LastRowNum; i++){
CategoryDB.CategoryData cDB = new CategoryDB.CategoryData ();
IRow row = sheet.GetRow (i);
if (row == null) {
Debug.Log (i + 1 + "行目のデータは存在しませんでした");
break;
} else {
cDB.id = (int)row.GetCell(0).NumericCellValue;
cDB.name = (string)row.GetCell(1).StringCellValue;
cDB.sentence = (string)row.GetCell(2).StringCellValue;
data.list.Add (cDB);
}
}
}
}
view raw Importer.cs hosted with ❤ by GitHub

0 件のコメント:

コメントを投稿