*xlsxは不可能になった模様(xlsなら可能)
*スクリプトの全容は最後に紹介
手順概要
- AssetPostprocessor.OnPostprocessAllAssetsを用いて、Excelファイルがインポートされた時に処理を行う(所謂エディタ拡張を行う)
- ファイルを開く(System.IO)
- 開いたファイルを読み込む(ExcelデータをC#で扱うためにNPOIが必要となる)
- データを元にScriptableObjectを作成(どのようなパラメーターを持つかは事前に作成しておく:参考)
- ScriptableObjectをアセットとして保存
1. AssetPostprocessor.OnPostprocessAllAssetsについて
使える引数が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フォルダに入れておけばビルド時にゲームには同梱されずに済む。
名前空間について
- ファイルの読み書きをするので、System.IOが必要。
- IWorkBookを使うために、NPOI.SS.UserModelが必要
xlsxの読み書きにはNPOI.XSSF.UserModelが必要恐らく不可能になった- 旧式ファイル形式(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でエクセルのファイルを作った方が良さそう。この時点であまり将来性のない技術になってしまったかもしれない。
まずは既に出力されているか確認する。ロードしてみてNULLが返ってくるようなら新規作成する。その後は、ファイルが既にあろうがなかろうがリストを破棄して、最新の状態に書き換える。WriteSOは自作メソッドであり、これについては後述する。
上図に出てきたCategoryDBとは、ScriptableObjectを継承した自作のクラス。作り方は前回の記事参照(前回は別なクラスを例にとっているがやっている事は同じ)。
備考
■ Step 2
Step1で目的のアセットファイルをロードもしくは新規作成した。次にこのファイルに対して書き込みを行う。書き込みの処理を行うWriteSOは、次のようなメソッドである。
Excelに関わる処理はNOPIで行う。このNPOIは、Java用に作られたPOIを元に作られている。なので使い方はPOI のAPIを参考にすることになる。
注意点
■ Step 3
4. データを元にScriptableObjectを作成
■ Step 1まずは既に出力されているか確認する。ロードしてみてNULLが返ってくるようなら新規作成する。その後は、ファイルが既にあろうがなかろうがリストを破棄して、最新の状態に書き換える。WriteSOは自作メソッドであり、これについては後述する。
上図に出てきたCategoryDBとは、ScriptableObjectを継承した自作のクラス。作り方は前回の記事参照(前回は別なクラスを例にとっているがやっている事は同じ)。
備考
- このクラスはゲーム内で使用するためEditorには含める事はできないので注意
- 今回はメニューから作るわけではないので[CreateAssetMenu()]は不要
Step1で目的のアセットファイルをロードもしくは新規作成した。次にこのファイルに対して書き込みを行う。書き込みの処理を行うWriteSOは、次のようなメソッドである。
Excelに関わる処理はNOPIで行う。このNPOIは、Java用に作られたPOIを元に作られている。なので使い方はPOI のAPIを参考にすることになる。
注意点
- シートも行列も数え始めは0である。例えばセルの1行目は、NOPIでは0行目である。
- 上記スクリプトのように列→行の順であり、この逆はできない
なお、今回使用したExcelファイルの中身は次の通りである。
■ Step 3
今回加えたいパラメーター群は、CategoryDBクラスの内部クラスであるCategoryDataである。なので、Step2のスクリプトの53行目でこのクラスを生成し、59~61行目でExcelのセルのセルの値を取得している。
こうして出来上がったCategoryDataを、Step1で用意したdataのリストに加えていく。リストに加えるにはAddを使う。
スクリプトの全容は下記の通り。
こうして出来上がったCategoryDataを、Step1で用意したdataのリストに加えていく。リストに加えるにはAddを使う。
終わりに
以上の作業で、ExcelからScriptableObjectのオブジェクトを作成できる。xlsファイルをインポートすると指定したパスに、Excelの内容が反映されたファイルが自動生成される。スクリプトの全容は下記の通り。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} | |
} |
0 件のコメント:
コメントを投稿