UnityにはAddressable Asset System(AAS)というアセット管理システムがあります。
AASはアセットをパスではなくアドレスと呼ばれる識別子で管理できる点が従来のアセット管理システムと比べて非常に便利なのですが、通常の使い方ではUnityエディタ拡張で使うことができません。
一方、Unityのエディタ拡張ではUI Elements用のUXMLファイルやUSSファイルなどの多くのアセットを扱います。これらのエディタ用アセットをパスに依存せずに管理できるようにするため、エディタ拡張でもAASを使いたい、というのがこの記事の趣旨です。
Table of Contents(目次)
Addressable Asset System(AAS)
端的に言えば、Addressable Asset System(AAS)はアセットにアドレス(=アセットを一意に認識するための識別子)を付与しておき、ソースコードから参照する際にアドレスを指定してアセットを受け取ることができるシステムです。
また、こちらは私自身は未検証なのですが、ローカル上のアセットもリモートのアセットバンドルも同一インターフェースでアクセスできる点もAddressable Asset Systemの便利なポイントのようです。
これにより、以下のような悩みから開発者を解放してくれます。
- アセットのパスがソースコードと密結合になっていて、アセットを移動するとコードも変更する必要がある
- ソースコード上でアセットの置き場所(ローカル/リモート)を意識しなくともよい
Addressable Asset Systemの詳細については私も学びたてなので、これ以上の詳細については公式ドキュメントと、より詳しく書かれている他サイトの記事を紹介するに留めます。
基本的な概念や使い方はこれらのサイトを参照してください。
Addressableをエディタ拡張で利用する方法
本題です。
冒頭にも書いた通り、特にUI Elementsを用いたエディタ拡張では多くのUXML/USSを作り、ソースコードから参照することになります。
以下はその一例です。
public class ExampleWindow : EditorWindow
{
[MenuItem("Example/Show Window")]
static void Open()
{
GetWindow<ExampleWindow>();
}
private void OnEnable()
{
var root = rootVisualElement;
// UIの構造を適用
var uxml = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("UXMLファイルへのパス");
uxml.CloneTree(root);
// スタイルを適用
var uss = AssetDatabase.LoadAssetAtPath<StyleSheet>("USSファイルへのパス");
root.styleSheets.Add(uss);
}
}
プロジェクトの構造が決まりきっていて、パスが2度と変わらない場合はこれでも問題ありません。ただ、もしプロジェクトの構造が変わってしまった場合、1つ1つパスを書き換えていくのは非常に面倒です。
そこで、「Addressable Asset System(AAS)をエディタ拡張でも使えれば、パスが変わってもアドレスさえ同じであれば参照が切れないじゃないか」と思ったので、以下のようにしてみました。
…が、以下のソースコードは動きません。なぜなら、Addressables.LoadAssetAsync
はエディタ拡張時には使えないためです。なんてこった。
public class ExampleWindow : EditorWindow
{
[MenuItem("Example/Show Window")]
static void Open()
{
GetWindow<ExampleWindow>();
}
private async void OnEnable()
{
var root = rootVisualElement;
// UIの構造を適用
var uxml = await Addressables.LoadAssetAsync<VisualTreeAsset>("AASに登録したUXMLのアドレス").Task;
uxml.CloneTree(root);
// スタイルを適用
var uss = await Addressables.LoadAssetAsync<StyleSheet>("AASに登録したUSSのアドレス").Task;
root.styleSheets.Add(uss);
}
}
意地になって調査してみたところ、どうやら「AASで管理されているアセットの一覧」を取得することはエディタ拡張でもできそうだったので、独自に以下のような関数を作って対応することにしました。
public static T LoadAssetByAddress<T>(string address) where T : UnityEngine.Object
{
var setting = AddressableAssetSettingsDefaultObject.GetSettings(false);
var entries = new List<AddressableAssetEntry>();
setting.GetAllAssets(entries, false);
foreach (var entry in entries)
{
if (entry.address == address)
{
return AssetDatabase.LoadAssetAtPath<T>(AssetDatabase.GUIDToAssetPath(entry.guid));
}
}
return null;
}
アセットを全て読み出してループして検索…という力技ではありますが、これはエディタ拡張でも動作します。この関数を使うと、以下のようにアドレスからアセットを読み込むことができます。
public class ExampleWindow : EditorWindow
{
[MenuItem("Example/Show Window")]
static void Open()
{
GetWindow<ExampleWindow>();
}
private void OnEnable()
{
var root = rootVisualElement;
// UIの構造を適用
var uxml = LoadAssetByAddress<VisualTreeAsset>("AASに登録したUXMLのアドレス");
uxml.CloneTree(root);
// スタイルを適用
var uss = LoadAssetByAddress<StyleSheet>("AASに登録したUSSのアドレス");
root.styleSheets.Add(uss);
}
public static T LoadAssetByAddress<T>(string address) where T : UnityEngine.Object
{
var setting = AddressableAssetSettingsDefaultObject.GetSettings(false);
var entries = new List<AddressableAssetEntry>();
setting.GetAllAssets(entries, false);
foreach (var entry in entries)
{
if (entry.address == address)
{
return AssetDatabase.LoadAssetAtPath<T>(AssetDatabase.GUIDToAssetPath(entry.guid));
}
}
return null;
}
}
おわりに
これで、エディタ拡張でも「アドレス」というパスに依存しない値でアセットが管理できるようになりました!
もしもっとよい方法をご存知の方がいればDMなどで教えてください(Twitter: @mido-app)。
そもそも「エディタ拡張ではAASが使えないよ」という情報にたどり着くのにめちゃくちゃ時間がかかった…。
以下に調べた時に出てきたサイトを貼っておきますね。