今回はUnityが標準機能的には非対応なフォーマットのデータをインポートした際、まるで対応フォーマットのようにファイル自体をアセットとして使用可能にする「Scripted Importer」という機能の紹介です。
なお、ScriptableではなくScripted Importerです。
Unityが対応しているフォーマット
Unityはプロジェクトビューにファイルを放り込むと、アセットとして認識されドラッグ&ドロップ等の方法で使用できるようになります。
例えばFBXやOBJといったモデル、テクスチャー、オーディオ系のファイル等など。
そしてUnityはアセットとして認識したファイルは、表ではファイルの形を保持しつつUnityの使用しやすいファイル形式…大抵はScriptableObjectやObject(中身はネイティブ)に変換されます。
これによって、FBXやPSD形式といったランタイムではとても使い勝手の難しい冗長なフォーマットを、速やかに使えるようになる訳です。
Unity非対応フォーマットはインポーターで別途ファイルを作る
ではUnity非対応なファイルはどうでしょう。例えばエクセルのファイルやCSVといったデータです。
残念ながら、そういったファイルはアセットとは識別されません。
これらを実行時に解釈してC#で使えるフォーマットに変換するのは、ライブラリさえあれば可能でしょうが、プラットフォームに対応した読込ライブラリや、読み込む際の追加コストが必要になります。
そういったケースではインポーターを実装し、読みやすいデータを別途作成してやるのがよくやる手法です。
例えばXLSをScriptableObjectに変換すれば、ランタイムにExcelを読み込むライブラリを含める必要は無くなります。
ただしこのケースでは、ファイルの移動や削除も観測する必要が出てきます。
Scripted Importerはもう一歩進んだ機能で、ファイルそれ自体をアセットとして認識させてしまおうという機能です。
ScriptedImporterはファイルをアセットにする
Scripted Importerは、ファイルをアセットのように認識させる機能です。
正確に言えば、ファイルに紐付けてGameObjectやScriptableObjectを登録しアセットとして認識させ、プロジェクトビューからドラッグ&ドロップで使用できるようにする機能…というのが近いかもしれません。
実際に使ってみる
実際に動きを見てみます。
例えば、拡張子が「.cube」という適当なファイルを用意します。この中身はJSONで、スケールのみが記載されています。
想定では、このCubeは内包されているVector3を使用してCubeを作成します。下の図だと、xが1,yが5、zが3のCubeです。
このファイルをインポーターを実装したエディターのプロジェクトビューに配置すると、速やかにGameObjectに変換されます。
そしてドラッグ&ドロップでシーンに配置も出来る感じになります。
そして元データを書き換えると、即座に変更が反映されます。
なお他の独自Importer系と異なり、ファイルを動かすのが楽な点がかなり個人的に好みです。
Scripted Importerの使い方
Scripted ImporterをEditorフォルダ以下に作成します。
ScriptedImporterのコードはこんな感じです。
あとは".cube"の拡張子のファイルをプロジェクト内に配置すれば、GameObjectのCubeに変換されます。同時にファイル内のJSONの値を利用してスケールが設定されます。
コードを少し解説しましょう。
ScriptedImporterAttributeとScriptedImporter
まず重要なのが、ScriptedImporterAttribute属性(左の矢印)を持っている事です。
ここで指定されている拡張子が、インポーターに反応するようになります。
また同時にScriptedImporter(紛らわしい!)を継承している必要があります。
なに?namespaceが分からない?エディターの機能で解決するのだ
登録の様子。Visual Studioも似たような機能があった記憶があります。
ファイルにオブジェクトを登録する
次にファイルにオブジェクトを登録する手順です。
ここでは、AddObjectToAssetでアセットを登録すればOKです。
その際にMainObjectに登録したアセットがHierarchyやSceneにドラッグした時に使用されるアセットになります。
以下ポイント
- AddObjectToAssetの第一引数はユニークな値を指定します。
- またSetMainObjectはAddObjectToAssetで登録したアセットの一覧からのみ設定できます。
- 基本的にAddObjectToAssetやSetMainObjectは毎回やる必要があります。
- コード的には毎回オブジェクトを作ってますが、2017.3では参照は維持されました。以前は維持されませんでした。
なお、この実装だとインポートの度にオブジェクトが作られます。例えば今回の例だとマテリアルの設定を変更してもインポートの度にマテリアルが新しく作られてしまい、変更が反映されません。
これを回避したい場合は、サンプルコードのように変更してやれば勝手に更新されないようになります。
https://gist.github.com/tsubaki/e7280fe3f02665089d6499669b7483d4
データの登録は普通に展開して登録する
次に登録すべきデータの読み出しです。
ここは普通にロードすれば良いです。
勿論、普通に読めないデータは読めるようなライブラリなどを入れる必要がありますが、エディター内で完結するライブラリなのでそれ程問題はないでしょう。FBXSDKだって使えます。
この時、try-catchで例外は握りつぶしておかないと、データが変なときに変な動きになる事があります。
ex.データの追加設定
インポートした後にデータ毎に若干設定を加えたいようなケースもあります。例えばモデルのインポーターには多種多様な設定があります。
その場合、ScriptedImporterにシリアライズ可能な変数を用意すると、外部設定として保持できます。あとは変更した値に応じてインポーターの中身を書き換えてやれば、細かい微調整も可能そうです。
下の例では、インポート後に生成するプリミティブなモデルを変数で変更しています。
https://gist.github.com/tsubaki/1965263ad6cdf8f3c454c76f0a382d47
なお、ScriptedObjectのInspector自体はScriptedImporterEditorで拡張出来るのですが、それを反映(Apply)させる方法がわからない。うーむ
感想
今回はGameObjectを作りましたが、普通はScriptableObjectを作ったりするものでしょう。
テキストや自分の好みの情報を元に、ScriptableObjectやPrefab、Meshを作成できるこの技術は、割りと良い感じに見えます。
ただcsvやjsonといったファイルや既に使用されていて動かなかったような気がするので別の拡張子を割り当てる必要があったような無かったような。はてさて。
なお、この機能はExperimentalな機能なので、今後変わるかもしれません。この記事ではUnity 2017.3f2で試しています。
関連
モデルを作る例。
この例だとオブジェクトを結合するだけですが、上手く使えば…
属性について。いい加減この記事メンテしなければ…
同じくらい個人的に有益だなと思う拡張
幾つか参考になります。