【Unity】CSVファイルにデータを書き込むEditor拡張サンプル

【Unity】CSVファイルにデータを書き込むEditor拡張サンプル

以前、CSVファイルをUnityのプロジェクトにインポートし、それを読み込んでScriptableObjectにデータを格納するサンプルを紹介しました。

「読み込みがあるなら書き込みもあるよね」

という考えのもと、Unityで何らかのデータをCSVファイルに書き込むEditor拡張のサンプルを扱います。

 

環境

macOS 10.14 Mojave

Unity2018.2.20f1

もういい加減2018.3を使おうかな……。

CSVファイルに書き込むデータ

冒頭に紹介した記事の中で、敵キャラのデータを格納するScriptableObjectに以下のデータをセットしました。

名前 最大HP 攻撃力 防御力 経験値 ゴールド
スライム 4 2 2 1 2
ゴブリン 8 4 2 2 3
ジャイアントマウス 12 5 3 3 5
ScriptableObject側では以下のようにデータがセットされています。
ScriptableObjectの中身
ScriptableObjectの中身

 

今回はこのScriptableObjectに格納してあるデータを読み込んで、CSVファイルとしてアウトプットします。

なお割とガチガチにハードコーディングしながらサンプルを提示するのでご注意を。いきなりReflectionを使ってEditor拡張し始めたら初心者ドン引きですものね。

それはまた中級編として別にまとめます。

作業の流れ

今回のサンプルで行う作業は以下の通り。

(0. データ格納用のScriptableObject継承クラスを作成)

  1. CSVファイル書き出しボタンを表示するためのScriptableObjectを作成
  2. 上記ファイルのInspector表示と書き出し動作を定義するEditor拡張スクリプトを作成
  3. ボタンを押してCSVファイルが書き出されたことを確認

わずか3ステップ! ね? 簡単でしょう? (白目)

0.のデータ格納用クラスについては、以下の記事の『データ格納用のScriptableObjectを作成』をご参照ください。

ScriptableObjectの作成自体はすっごく簡単です。

 

CSVファイル書き出し用ScriptableObjectを作成

Editor拡張を行う場合は、自分で新しくウィンドウを作ったり、Inspectorウィンドウを改造したりして何らかの作業をすることが多いかと思います。ちょっとした機能のために新しくウィンドウを作るのは大変なので、今回はInspectorウィンドウを改造する方法でいきましょ。

Inspectorウィンドウを改造する場合はScriptableObjectを新たに作って、その中身を表示する部分で手を加えるのが楽でいいかと思います。

まずはスクリプトファイルの作成から。Projectウィンドウで任意のフォルダで右クリックして[Create] -> [C# Script]を選択します。

スクリプトファイルの作成
スクリプトファイルの作成

 

名前はそれっぽく『CsvExporter』にしました。

CsvExporter
CsvExporter

 

CsvExporterの中身は以下のように編集します。

ScriptableObjectを継承するのがポイント。

CreateAssetMenuのAttributeを付けて、Createのメニューからアセットファイルを作成できるようにします。パスはご自身の分かりやすいものにしてもらえればOK。

クラスの中身はファイル名を持つフィールドのみとします。このアセットファイルが置かれた場所に、ここで定義されたファイル名を持つCSVファイルを作成します。

スクリプトを保存してUnityに戻ると、メニューから上記のScriptableObjectのアセットを作成できるようになります。上のスクリプトの例だと、[Create] -> [MyScriptable] ->[Create CSV Exporter]です。

アセットを作成するのだポッター
アセットを作成するのだポッター

 

画面の上にあるメニューバーからも作成可能です。

メニューバーからも
メニューバーからも

 

どちらからでもこのアセットファイルが作成されます。名前は好きに付けて大丈夫です。

アセットファイルができた
アセットファイルができた

 

このアセットファイルを選択してInspectorウィンドウを見るとこのようになっています。

Export用のアセット
Export用のアセット

 

上のスクリプトの中で定義したファイル名を入力するフィールドが表示されています。ここに出力用のファイル名を入力しておきましょ。今回は適当に分かりやすく「output.csv」にしました。業務系のシステムでよく見る名前ですね。

このCsvExporterのInspector表示をカスタマイズし、出力用のボタンを追加します。その方法はもちろん、Editor拡張。次はEditor拡張スクリプトをゴリゴリ書きます。

 

CSV出力処理をEditor拡張で実装

Editor拡張を行うときはスクリプトファイルを「Editor」フォルダの中に置きます。この「Editor」フォルダは特殊なフォルダで、ここに置かれたスクリプトはUnityエディタでのみ使うスクリプトとして扱われます。

Unityには「Editor」フォルダのように特殊なフォルダがあるので、以下のマニュアルも併せてご覧あれ。

 

プロジェクト内にいくつあっても、その「Editor」フォルダの中にあるスクリプトはビルド時に含まれない、Unityエディタ専用の機能となります。

Assets直下に1つだけ置く人もいますし、機能ごとにフォルダ分けしてその中で「Editor」フォルダを作る人もいます。Editor拡張系のアセットを作っている人だったら、パブリッシャー(アセットを作った人)の名前を付けたフォルダの中に「Editor」フォルダを作る人が多いかと思います。

というのも、別々のパブリッシャーのアセットをプロジェクトにインポートした際、Assets直下の「Editor」フォルダに配置されるようにすると他の人のEditor拡張スクリプトと混ざっちゃうんですよね。どのアセットで使っているスクリプトか分からない状態ってめちゃくちゃ怖いので、なるべくこうならないのが理想です。

ちょっと話が逸れましたが、「Editor」フォルダはプロジェクト内で複数作っても大丈夫なので、今回は「ExportCSV」フォルダの中に「Editor」フォルダを作ります。

フォルダを作成するのだポッター
フォルダを作成するのだポッター

 

フォルダを作成したらリネームして「Editor」に。

Editorフォルダの作成
Editorフォルダの作成

 

Inspectorでボタンを表示

続いて「Editor」フォルダの下に移動し、スクリプトファイルを作成します。右クリック -> [Create] -> [C# Script]を選択。

Editor拡張スクリプトの作成
Editor拡張スクリプトの作成

 

作成されたスクリプトは「CsvExportEditor」にリネームしました。安直ですが、分かりやすさ重視で。

安直
安直

 

これからスクリプトの中身を編集しますが、最初はボタンを表示するところまでやってみます。

スクリプトの説明については、以下のページの『Inspectorでボタンを表示』で説明しているものとほぼ同じなのでここでは割愛します。

 

スクリプトを保存してコンパイルが終わると、「CsvExporter」の表示が画像のようになります。

ボタンが追加された
ボタンが追加された

 

「敵データのエクスポート」ボタンをクリックするとコンソールにメッセージが出力されます。

ボタンを押すと出力される
ボタンを押すと出力される

 

Inspectorウィンドウで「CsvExporter」のアセットを表示する際に、上のスクリプトがちゃんと効果を発揮していることが分かりました。次はボタンを押した時の動作をCSVファイルの出力にしてみます。

 

CSVファイルの出力

ボタンを押した時の動作をCSVファイルの出力に切り替えます。

今回はハードコーディングでアレですが、出力する対象のデータをEnemyDataと決め打ちでやっちゃいます。

長いですが上から順番に説明させてください。

5行目では「using System.IO」を追加しました。これはファイルを読み書きするクラスが含まれている名前空間です。

15行目はデバッグ文を表示していた部分を後述のOutputCsvメソッドの呼び出しに変更しました。

19行目から始まるOutputCsvメソッドではScriptableObjectのデータを読み込み、その内容をCSVファイルに書き込んでいます。最初の部分はファイル名が入力されているかのチェックです。

26行目ではプロジェクト内の「EnemyData」アセットを検索しています。引数で渡している”t:EnemyData”が検索用のフィルター文字列になっていて、“t:”はクラス名検索をしたいときに使います。

戻り値はアセットのGUID文字列なので、それを使ってforeachの中でアセットのパスを取得し、今回読み込みたいEnemyDataをロードしています。ロードしたデータは順次リストに突っ込んでいます。

37行目からは出力先のパスを決めています。最初にこのスクリプトの処理を呼び出しているアセットファイルのパスを取得し、そのアセットファイルがいるフォルダ名をSubstringで取得します。そのフォルダの中に、「CsvExporter」で指定したファイル名のCSVファイルを作成しています。

ここでFileInfoやStreamWriterを使いたかったので、「using System.IO」を指定する必要があったんですね。

42行目、43行目ではCSVのヘッダ行を出力しています。

46行目からは、26行目から始まるブロックで読み込んだEnemyDataの中身をファイルに出力しています。個人的にループの中ではStringBuilderを使ってAppendしていくのが好きなので、こんな形で各フィールドの中身を出力しています。

今回はデータの中に含めていませんが、敵キャラなどのID順で出力したい場合は46行目のループの前に、LinqのOrderByなどでIDによるソートをしておくといい感じです。

foreachのループが終わったらストリームを閉じて、AssetDatabaseをリフレッシュして終了です。

実際にやってみる

先ほどのスクリプトを保存し、Unityエディタに戻ってコンパイルを終了させます。

Projectウィンドウで「CsvExporter」を選択し、File Nameを空にして「敵データのエクスポート」ボタンをクリックしてみます。

空文字列の場合
空文字列の場合

 

するとコンソールにちゃんと警告が表示されました。うまくいっていそうですね。

警告が表示された
警告が表示された

 

続いてFile Nameに任意の名前を入力します。上の方では「output.csv」と拡張子までユーザ側で入力するようにしましたが、スクリプトの38行目で機械的に拡張子を付ける処理を入れたので、このフィールドでは拡張子を付けなくても大丈夫です。入力が終わったらボタンを押しましょ。

ファイル名を入力してポチッとな
ファイル名を入力してポチッとな

 

ボタンを押すとコンソールにメッセージが表示されました。データ数が少ないのですぐに終わるかも。

完了のメッセージ
完了のメッセージ

 

Projectウィンドウでも表示されました。AssetDatabase.Refresh()を呼ばないと表示されるまで時間がかかるので注意。

Projectウィンドウでも見えた
Projectウィンドウでも見えた

 

出力した「output」ファイルを選択し、Inspectorウィンドウで確認します。

ScriptableObjectの内容が出力された
ScriptableObjectの内容が出力された

 

CSVファイルの内容は冒頭で紹介した以下のデータと一致していました。ちゃんとCSVに書けてますね。

名前 最大HP 攻撃力 防御力 経験値 ゴールド
スライム 4 2 2 1 2
ゴブリン 8 4 2 2 3
ジャイアントマウス 12 5 3 3 5

 

ScriptableObjectに入っているデータは以下の通り。

ScriptableObjectの中身
ScriptableObjectの中身

 

試しに出力されたファイルを表計算ソフトで開いてみるとこんな感じ。別のソフトウェアでも開くことができたので良さそうです。

MacのNumbersで開いてみる
MacのNumbersで開いてみる

 

まとめ

ScriptableObjectのデータをCSVファイルに出力するEditor拡張のサンプルを紹介しました。

ちょっとハードコーディングが目立ちますが、出力したいデータの種類が少なければこれでもいいかなーと。

動的に変えたいときは、以下の部分を修正するといい感じ。

  • 26行目のタイプ検索
  • 27行目のデータ格納用クラス
  • 42行目のヘッダー行
  • 46行目からの出力するフィールド

この辺りを変数などで渡せれば使い回しもできるかも。

アプリ公開までの攻略チャートを作りました!

CTA-IMAGE

「スマホ向けのアプリを作ってみたいけど、何から手を付けていいか分からない!」


そんなお悩みをお持ちの方向けに、todoがアプリをリリースした経験から手順や考慮すべき点をまとめたe-bookを作成しました。ゲーム作りはそれ自体がゲームのように楽しいプロセスなので、「攻略チャート」と名付けています。


アプリをリリースする観点から書いているので各アプリストア向けの作業が入っていますが、企画、設計、開発、テスト部分については他のプラットフォームでも使える知識が満載です。


無料で入手できるのでぜひお早めにゲットして、開発をブーストさせてください!