大元のアイデアはテラシュールブログさんです(かなり昔)。エクセルを自動的に Scriptable Object にして楽々データ管理、2020年版です。
後発ですし、ちまちまと機能追加もしていたら、結構コードが増えました…。
- マルチシート対応。1クラスマルチシートにも対応
- クラス内クラスも、エクセルで宣言可能
- List<> 構造も扱える
- enum 宣言もエクセルで可能
- enum やクラスのメンバにコメントを書くことも可能
そもそも Scriptable Object ってなに? という方はこちらの記事も参考にしてください。
今回のエディタ機能は Scriptable Object の欠点部分をなくし、もっと便利に使おうというプロジェクトです。
実行環境
実際にどう使うか確認するために、サンプルのエクセルも入っています。
以後の説明はこのパッケージを使ったものです。解凍し、新しいプロジェクトで Import してください。
ImportXLS1007 ダウンロード
最新は Github にてダウンロードできます。
NPOI ライブラリが必要
エクセルにアクセスするため、NPOI ライブラリを使用しています。
同梱するのはさすがにまずそうなので、申し訳ありませんがパッケージには含まれていません。
Editor/ImportXLS/npoi の中にいれてください。
2020/09 現在のダウンロード先と方法を示しておきます。必要なのは 6 ファイルです。
NPOI.dll
NPOI.OOXML.dll
NPOI.OpenXml4Net.dll
NPOI.OpenXmlFormats.dll
https://www.nuget.org/packages/NPOI/2.5.1
(解凍したフォルダの lib/net45 内)
BouncyCastle.Crypto.dll
https://www.nuget.org/packages/Portable.BouncyCastle/
(解凍したフォルダの lib/net40 内)
ICSharpCode.SharpZipLib.dll
https://www.nuget.org/packages/SharpZipLib/
(解凍したフォルダの lib/net45 内)
ページの右欄、Download Package でそれぞれ入手できます。
無事に入れられた場合、こんな感じになっているはずです。
サンプルエクセルの説明
実際のRPGっぽいサンプルデータです。マップチップと、2つの街のキャラ情報が含まれます。
列
Scriptable Object を扱うためのクラスメンバです。
必ず一番最初に ID 列を宣言する必要があります(つけないとコンバートされません)
列の型
エクセルから自動的に型を取ろうとも考えたのですが、色々と火種を産むのでやめました。列のプログラム上の型を指定してください。
bool、int、string、float、enum が使えます。
bool
基本は false / true 。空欄は false となり、〇など「なにか文字列があれば」true 扱いです。
int
float
数字。エクセルで =SUM(A1:A2) など計算式を設定していた場合、その結果を取得できます。
string
文字列。エクセルで =TEXT(A1,"###")+TEXT(A2, "###") など式を設定していた場合、その結果を取得できます。
enum
基本は enum:eQuestionCategory と記述しますが、別クラスの enum を使う場合、クラス名.enum名 です。例えば enum:CityData.eMovePattern みたいな。
List<> 構造やサブクラスも可能ですが、ややこしくなるので「その他の仕様」にて。
コメント
#comment
//comment
[[comment]]
これらの記号で記述されていたものをコメントと見なします。列であれば上、enum であれば右に書いてある場合のみ有効です。
コメントはなくても構いません。
背景色は別に黒でなくていいです。サンプルはわかりやすいように色を変えてあります。
テーブル (Scriptable Object)
Scriptable Object のことを本記事ではテーブル、と呼びます。一般的には「表」でしょうか。
SQL などをごにょごにょした経験のある方はそちらの方がネーミング的にしっくりくるかも。
enum
シートにはテーブルだけではなく、列に使った enum を宣言することもできます。
テーブルを記述しなければ、enum だけのシートなんてのも作れます。(使うかどうかはおいといて)
テーブルと enum、enum と enum の間は最低 2 行空けるようにしてください。
空けない場合、エラーログが出てそのシートはコンバートされません。
なお、[ENUM] ではなく [GLOBAL_ENUM] とするとクラスの外に enum が生成されます。
クラスの外なので、他のクラスからも簡単にアクセスできるようになります。
///<summary> /// eConst ///</summary> public enum eLang { 大きい方, 小さい方, }; /// <summary> /// Table: ScriptableObject /// </summary> public class Table : ScriptableObject { ///<summary> /// Table Row ///</summary> [System.Serializable] public class Row ・ ・ ・ }
MapChipシートを変換する
MapChip を Scriptable Object に変換する手順です。
- エクセル TestGame を 右クリック - ImportXLS(おそらく一番下)を選択
- class dir、scriptable object dir は デフォルト のまま
- ウィンドウ下、真ん中の create MapChip ボタンを押す
正しく作成されると、次のようなウィンドウが表示されます。
自動的に 3 ファイルほど追加されます。これが Scriptable Object を生成したり、アクセスするためのキークラスとなります。詳細については後程説明します。
準備は整ったので、Scriptable Object を作成します。手順は 1 つだけです。
- TestGame を 右クリック - Reimport を選択
Console ウィンドウに [ImportXLS] create 'Assets/Resources/MapChip.asset' と表示され、Scriptable Object が生成されます。
オレンジの部分がコンバート内容です。
Scriptable Object は手で編集できないようにロックがかかっています。
Scriptable Object を手で変更してしまうと、エクセルが更新されるたびに上書きされ変更が消されたり、わけがわからなくなるためです。
Scriptable Object の型となるクラスは MapChip_Table.cs です。テーブルだけではなく、enum の情報なども自動的に取り込まれています。
エクセルに記述されていたコメントも、ソースコードに反映されます。
(エクセルにコメントをかかなければ、ソースコードにコメントが入ることもありません)
全シートを変換する
先ほどと、手順はたいして変わりません。
- エクセル TestGame を 右クリック - ImportXLS を選択
- CREATE ALL を押す
- TestGame を 右クリック - Reimport
また、エクセルを変更した後も自動的に Reimport が走り、Scriptable Object が更新されます。
通常はこちらの自動更新に任せておけば大丈夫です。
まれに自動更新されない場合があります。その場合今回のように、手動で Reimport しましょう。
自動生成されるクラスについて
MapChip を例に説明します。
MapChip_Table.cs
Scriptable Object のテーブルの形(列)を決めるクラスです。プログラムでは Row(行)クラスを使ってデータにアクセスします。
MapChip_Table test = Resources.Load<MapChip_Table>("MapChip"); foreach (var entity in test.Rows) { Debug.Log($"{entity.ID}: {entity.Category}"); }
Count
データの最大数を返します。
Rows
全行のデータを List<> で取得します。
GetRow(index)
配列の index 番目の行データを返します。
FindRowByID(id)
ID が id の行データを返します。
FindRowBy??????(val)
以下のように、列の後ろに * をつけると、FindRow メソッドが自動的に追加されます。
(下の例では FindRowByWord(eLang val) というメソッドが追加される)
MapChip.cs
データをシングルトンで扱いたい場合に使えるヘルパークラスです。
これを使えば、どのシーンでもインスタンスではなく MapChip.XXXX() でデータを呼び出せるので、扱いが格段に楽になります。
MapChip_Table test = Resources.Load<MapChip_Table>("MapChip"); MapChip.SetTable(test); foreach (var entity in MapChip.Rows) { Debug.Log($"{entity.ID}: {entity.Category}"); }
なお、このクラスがいらない場合、設定画面で create accessor のチェックを外せば、生成されません。
MapChip_Importer.cs
エクセルが更新されると unity はその情報を検知し、OnPostprocessAllAssets() を呼び出します。
この時にエクセルのシート情報から全て Scriptable Object に作っています。
自動で動作するので、このクラスを意識してアクセスすることはないでしょう。
なお、readonly な Scriptable Object にしているのはこのコードの以下の箇所です。
data.hideFlags = HideFlags.NotEditable;
その他の細かい使い方について
その他の細かい使い方については、こちらの記事をご覧ください。