Help us understand the problem. What is going on with this article?

Unity Addressableの重複アドレスや依存グループチェック機能を作る

はじめに

Addresabblesを使うには便利機能が足りていない部分があるので自作していきます。
Unity Addressable Asset Systemの使用方法と自動化の機能調査の続きとなる内容です。

使用バージョン
unity 2019.3.0b4
Addresabbles version1.5.0

スクリプトを作る

重複アドレスチェック

アセットにユニークなアドレスを設定しロードを簡単に書けるということですが、重複したアドレスを設定することができます。
この状態でもビルドは成功します。

アドレスが重複しているときは最後に追加されたアセットをロードしてきます。
このときも特に警告やエラーログは出ません。

アドレスの命名規則を決めておき重複しないように運用できていればいいですが、重複アドレスが無いかのチェックを行いましょう。

    /// <summary>
    /// 全アドレスの重複チェック
    /// </summary>
    [MenuItem(menuName + "/CheckAllAddress")]
    static public void CheckAllAddress()
    {
        var s = GetSettings();
        List<AddressableAssetEntry> entries = new List<AddressableAssetEntry>();
        s.GetAllAssets(entries, true);
        List<string> checkedAddress = new List<string>();
        foreach (var e in entries)
        {
            //チェック済みアドレスはコンテニュー
            if (checkedAddress.Contains(e.address)) continue;

            //全アセットで重複があるかチェック
            bool ret = CheckAddress(entries, e.address);
            if (!ret)
            {
                checkedAddress.Add(e.address);
            }
        }
    }

    /// <summary>
    /// 重複アドレスチェック
    /// </summary>
    /// <param name="entries"></param>
    /// <param name="address"></param>
    /// <returns>重複なしtrue、ありfalse</returns>
    static public bool CheckAddress(List<AddressableAssetEntry> entries, string address)
    {
        var s = GetSettings();
        var duplicateEntries = entries.FindAll(e=>e.address == address);
        if(duplicateEntries.Count > 1)
        {
            string str = "Address=" + address + System.Environment.NewLine;
            foreach (var e in duplicateEntries)
            {
                string assetname = System.IO.Path.GetFileName(e.AssetPath);
                str += "Group=" + e.parentGroup.Name + "," + "AssetName=" + assetname + System.Environment.NewLine;
            }
            Debug.LogAssertion("DuplicateAddress" + System.Environment.NewLine + str);
            return false;
        }
        return true;
    }

並び替え

AddresabblesGroupsのGUI上ではGroupを並び替えることが手動でできます。
Group内をPathで並び替えはできましたがなぜかGroup自体の並び替えはできませんでした。
image.png

内部的な並びは
AddressableAssetSettings.groupsのソートすれば変わるようですがGUI上は画面が更新されない限り
変わりませんでした。
GroupNameの欄をクリックするか、インポート時やコンパイル時に反映されました。

/// <summary>
    /// 文字列順にソートする
    /// </summary>
    [MenuItem(menuName + "/Sort")]
    static public void Sort()
    {
        var s = GetSettings();
        s.groups.Sort(new GroupCompare());
    }

文字列の大きさ比較してソートをするだけです。

このままでは不便すぎるので、Addresabblesのソースコードを書き換えてGUI上も更新させます。
スクリプトリファレンスのGUIのページにはAPIについて載っていないので勘で探します。
https://docs.unity3d.com/Packages/com.unity.addressables@1.5/api/UnityEditor.AddressableAssets.GUI.html

Addresabbles GroupsのGUIは
AddressableAssetEntryTreeView.cs
で描画されています。
UnityEditorのTreeViewを継承しています。
Reload関数を呼ぶことで強制的にデータを再読み込みさせられるとあるので呼べるようにしていきます。

AddressableAssetsSettingsGroupEditor.cs に
AddressableAssetEntryTreeView m_EntryTree; というメンバでインスタンスを持っており

AddresabblesAssetsWindow.cs に
AddressableAssetsSettingsGroupEditor m_GroupEditor; というメンバでインスタンスを持っています。
このAddressablesAssetWindowを取得して、TreeViewのReloadを呼び出します。

AddressableAssetWindowにReload関数を追加します。
1. AddressableAssetWindowクラスをpublicにする。
2. m_GroupEditor.Reload()を呼び出すpublic Reload関数を作る
3. エディタウィンドウを取得してReload関数を呼び出す。

/// <summary>
    /// 文字列順にソートする
    /// </summary>
    [MenuItem(menuName + "/Sort")]
    static public void Sort()
    {
        var s = GetSettings();
        s.groups.Sort(new GroupCompare());
        EditorWindow.GetWindow<UnityEditor.AddressableAssets.GUI.AddressableAssetsWindow>().Reload();
    }

これでソートを実行するとGUI上もソートできました。
image.png  image.png

空グループ削除

アセットの管理が変わってグループが必要なくなった時に、空グループは自動で削除されてほしい場合があります。
グループのアセットエントリ数をカウントして0ならば削除するだけです。
デフォルトグループの判定もあるので0かつデフォルトグループではない時に削除します。

    /// <summary>
    /// 空グループを削除
    /// </summary>
    [MenuItem(menuName+"/Remove/EmptyGroup")]
    static public void DeleteEmptyGroup()
    {
        var s = GetSettings();
        var groups = s.groups;
        foreach (var g in groups)
        {
            if (g.entries.Count == 0 && !g.IsDefaultGroup())
            {
                s.RemoveGroup(g);
            }   
        }
    }

依存関係チェック

アセットがどのグループに依存しているかをチェックする仕組みを作り、依存関係による巨大なバンドルが作られないようにします。
依存自体はAssetDatabase.GetDependenciesで取得できるので、このAPIとグループ内のエントリを調べていきます。

注意としてアセット数が千や万を超える膨大な数になると全アセット検索をしては数分かかるので、適宜フィルタリングをする必要が出てきます。

    [MenuItem("test/FindDependency")]
    static public void DependCheck()
    {
        var s = GetSettings();
        List<AddressableAssetEntry> entries = new List<AddressableAssetEntry>();
        s.GetAllAssets(entries, true);
        foreach (var e in entries)
        {
            string str = ("Group:" + e.parentGroup+  " Asset:" + e.AssetPath + System.Environment.NewLine);
            var dependencies = AssetDatabase.GetDependencies(e.AssetPath);
            foreach (var d in dependencies)
            {
                var guid = AssetDatabase.AssetPathToGUID(d);
                foreach (var g in s.groups)
                {
                    if (e.parentGroup == g) continue;

                    var entry = g.GetAssetEntry(guid);
                    if(entry != null && entry != e)
                    {
                        str += g.Name + System.Environment.NewLine;
                        str += d + System.Environment.NewLine;
                    }
                }

            }
            Debug.Log(str);
        }
    }

グループの設定はこの状態で、prefabB.prefabのスクリプトがprefabC.prefabを参照しています。
image.png
検索結果のログはこうなります。GroupCに依存しています。
image.png

おわりに

Addresabblesはver1.5.0になりバグも減っていると思います。
APIも十分あり処理をフックして自前の処理に置き換えることもできるので、それぞれのプロジェクトに応じて拡張をしていくことも可能です。
足りない機能はそれぞれ自作していきましょう。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした