【Unity】 AssetPostprocessorとかいう生産性爆上がりなヤツ
はじめに?
まあ、言うほど上がらないんですけどね。(そもそも使える場面が少ない)
はじめに
遅くなってしまい申し訳ない(土下座)。12/5分のアドカレです。
こんにちは!RiG++団体長のTenです。今日は、Unityでアセットのインポート前後に処理を挟ませる、AssetPostprocessorについてご紹介します!生産効率を爆上げしスピーディーに開発を進めましょう。
AssetPostprocessorとは
AssetPostprocessor を利用すると、インポートしたパイプラインをフックし、アセットをインポートする前と後にスクリプトを実行することが可能になります。
unity スクリプトリファレンス
だそうです。ちなみにここで言う「アセット」とは、unityのprojectタブに表示されているあらゆるファイル、ディレクトリのことです。
そんなAssetPostprocessorですが、以下の関数があります。
- OnPreprocess ~ (OnPreprocessModel, OnPreprocessAnimation など)
- アセットをインポートする前に呼び出される
- インポート時の設定を変更するときに使用する
- OnPostprocess ~ (OnPostprocessModel, OnPostprocessAnimation など)
- アセットがインポートされた後に呼び出される
- インポートされたアセットに対して変更したいときに使用する
- OnPostprocessAllAssets
- 全てのアセットインポートが完了した後に呼び出される
AssetPostprocessorの使いどころ
最初に、 AssetPostprocessorの使いどころについてですが、個人的には以下の2つになります。
- 何度も何度も何度も繰り返し行うアセットの初期設定
- 人力で行うには骨が折れる大量のアセット操作
具体的には、例えば
- 3Dモデルの初期設定
- csvで管理しているデータをunityで扱えるデータにする
今回は、後者の例について解説していこうと思います。
実際に使ってみた
今回やりたいこと
- csvで管理しているデータを1つずつスクリプタブルオブジェクトに変換する
- csvが更新される度にスクリプタブルオブジェクトを更新する
さて、今回はcsvの読込後に操作を加えたいので、OnPostprocessAllAssetsを使います。
実際に使用するスクリプトはこちらです。
using UnityEngine;
public class ScriptableData : ScriptableObject
{
public int ID;
public string Name;
public string Discription;
public Sprite Image;
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
public class Postprocessor : AssetPostprocessor
{
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (string str in importedAssets) // importedAssetsには、読み込まれた全アセットのパスが入る
{
if (str.IndexOf(".csv") != -1) // 読み込まれたファイルがcsvかどうか判定する
{
TextAsset textasset = AssetDatabase.LoadAssetAtPath<TextAsset>(str);
string assetfile = str.Replace(".csv", ".asset");
List<string[]> csvDatas = new List<string[]>();
StringReader reader = new StringReader(textasset.text);
// 準備
while (reader.Peek() != -1)
{
string line = reader.ReadLine(); // 一行ずつ読み込み
csvDatas.Add(line.Split(',')); // , 区切りでリストに追加
}
Dictionary<string, int> keyPare = new Dictionary<string, int>();
for (int i = 0; i < csvDatas[0].Length; i++)
{
keyPare.Add(csvDatas[0][i], i);
}
// 分かりやすくするため、項目名と番号のペアを作成
for (int i = 1; i < csvDatas.Count; i++)
{
assetfile = "Assets/ScriptableObject/" + csvDatas[i][keyPare["Name"]] + ".asset";
ScriptableData data = AssetDatabase.LoadAssetAtPath<ScriptableData>(assetfile);
if (data == null)
{
data = ScriptableObject.CreateInstance("ScriptableData") as ScriptableData;
AssetDatabase.CreateAsset(data, assetfile);
}
// 対象となるスクリプタブルオブジェクトを検索、無ければ作成
data.ID = Convert.ToInt32(csvDatas[i][keyPare["ID"]]);
data.Name = csvDatas[i][keyPare["Name"]];
data.Discription = csvDatas[i][keyPare["Discription"]];
data.Image = AssetDatabase.LoadAssetAtPath<Sprite>(csvDatas[i][keyPare["Image"]]);
// 項目を更新
EditorUtility.SetDirty(data); // 保存処理
}
AssetDatabase.SaveAssets(); // 保存
Debug.Log("完了");
}
}
}
}
PostProcessor.csをEditorディレクトリに配置して、以下のcsvファイルをインポートしてみます。
すると……
と、このようにスクリプタブルオブジェクトが自動的に生成されました。
続いて、csvを更新してみましょう。
今回は”Name”の項目でパスを作成しているので、それ以外の項目を全て変更してみます。
変更後のcsvファイルはこちらです。
すると、次のように、スクリプタブルオブジェクトが更新されました。
注意点
今回はcsvファイルを編集した後、スクリプタブルオブジェクトに変更を加えるという手順だったので、問題になりませんでしたが、インポートするアセットと変更を加えるアセットが同じ種類のファイルである場合には1点注意が必要です。
というのも、AssetPostprocessorはファイルのインポート時に反応すると言いましたが、それにはファイルの更新時なども含まれているからです。
例えば、テクスチャの初期設定を行うため、テクスチャ用のAssetPostprocessorを作成すると、初期設定とは違う設定にテクスチャを変更しようとした際に、自動的に初期設定に戻してしまう恐れがあります。
この問題を解決するには、例えば、メタファイルの有無で処理を変更してあげるなどの対策が必要になります。
おわりに
ということで、簡単にですが、AssetPostprocessorの紹介でした。使う機会はあまりありませんが(エディタ拡張なんてだいたいそういうもん)、覚えておいて損はないと思うので、頭の片隅にでも置いておいて下さい。
……今さらなんですが、もう少しファイル操作やAssetDatabaseの話をしてからにすべきだったかもしれないですね。まあその辺は各自で学んでくれってことで。
というか本当はAssetPostprocessorじゃなくてDOTSの話をする予定だったんですが……。まあ間に合わなかったんで仕方ないですね。来週辺りに書こうかと思います。
さて、明日(既に今日です)はBluckからBitSummitのお話があるようです。お楽しみに。