Raspberlyのブログ

Raspberlyのブログ

Unityネタをメインとした技術系ブログです。にゃんこ大戦争や日常なども。そろそろブログタイトル決めたい

【エディター拡張】Projectビューの指定のフォルダにあるPrefabをListに格納する【Unity】

今回はUnityのエディター拡張をしていきます。
最近エディター拡張を書くことが多いので、自分用の忘備録です。

 

やること

Projectビューの指定のフォルダにあるPrefabを検索し、それをScriptのListに格納する

 

 

開発環境

Unity 2021.3.12f1

 

 

事前準備

先に格納したいPrefabを作成します。
Assets/Prefabsフォルダを作成し、適当にGameObjectをPrefab化しておきます。

 

 

 

Scriptの作成

以下のScriptを作成します。

ObjectList.cs

GameObject型のListがあるScript。
Listを設定する関数と空にする関数を持つ。

using System.Collections.Generic;
using UnityEngine;

public class ObjectList : MonoBehaviour
{
    [SerializeField] List<GameObject> m_List = new List<GameObject>();

    /// <summary>
    /// GameObjectのListを受け取りm_Listに格納する
    /// </summary>
    /// <param name="objects"></param>
    public void SetObjects(List<GameObject> objects)
    {
        m_List = objects;
    }

    /// <summary>
    /// m_Listを空にする
    /// </summary>
    public void Clear()
    {
        m_List.Clear();
    }
}

 

 

ObjectListEditor.cs

ObjectList.csのエディター拡張Script
プレハブをロードしてListに格納するボタンと、Listを空にするボタンを表示する。

using System.Linq;
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(ObjectList))]
public class ObjectListEditor : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        var objectList = target as ObjectList;

        if(GUILayout.Button("プレハブロード"))
        {
            LoadPrefabs(objectList);
            EditorUtility.SetDirty(objectList);
        }

        if(GUILayout.Button("クリア"))
        {
            objectList.Clear();
            EditorUtility.SetDirty(objectList);
        }
    }


    /// <summary>
    /// プレハブのロード
    /// </summary>
    static public void LoadPrefabs(ObjectList objectList)
    {
        // ロード先のディレクトリパス
        var path = "Assets/Prefabs";

        var guids = AssetDatabase.FindAssets("t:GameObject", new string[] { path });
        var paths = guids.Select(guid => AssetDatabase.GUIDToAssetPath(guid)).ToArray();
        var list = paths.Select(_ => AssetDatabase.LoadAssetAtPath<GameObject>(_)).ToList();

        if(list.Count == 0)
        {
            Debug.LogWarning("プレハブがない\n" + "検索パス[" + path + "]");
            return;
        }

        Debug.Log("プレハブを検索:" + list.Count + "つ格納\n" + "検索パス[" + path + "]");
        objectList.SetObjects(list);
    }

}

これで完成

 

実行

空のGameObjectを作成し、ObjectListをアタッチするとボタンが表示されます。
ロードボタンを押すと、指定のフォルダにあるPrefabをListに格納します



 

 

調整

以下細かい調整

読み込むフォルダの変更

ObjectListEditor.csのLoadPrefabsにある

var path = "Assets/Prefabs";

ディレクトリパスを指定しているのでここを書き換えればよし。
ここも指定できるようにしていいかも。

 

 

特定のコンポーネントがアタッチされているPrefabのみ格納する

Prefabを検索する時、特定コンポーネントがあるかWhere句を挟むとよい。
例えば、RigidbodyがアタッチされているPrefabだけ格納したいならこうする。

        // 特定のコンポーネントがあるものだけ格納
        var list = paths.Select(_ => AssetDatabase.LoadAssetAtPath<GameObject>(_))
            .Where(_ => _.TryGetComponent(out Rigidbody rigidbody))
            .ToList();

 

    /// <summary>
    /// プレハブのロード
    /// </summary>
    static public void LoadPrefabs(ObjectList objectList)
    {
        // ロード先のディレクトリパス
        var path = "Assets/Prefabs";

        var guids = AssetDatabase.FindAssets("t:GameObject", new string[] { path });
        var paths = guids.Select(guid => AssetDatabase.GUIDToAssetPath(guid)).ToArray();

        // 特定のコンポーネントがあるものだけ格納
        var list = paths.Select(_ => AssetDatabase.LoadAssetAtPath<GameObject>(_))
            .Where(_ => _.TryGetComponent(out Rigidbody rigidbody))
            .ToList();

        if(list.Count == 0)
        {
            Debug.LogWarning("プレハブがない\n" + "検索パス[" + path + "]");
            return;
        }

        Debug.Log("プレハブを検索:" + list.Count + "つ格納\n" + "検索パス[" + path + "]");
        objectList.SetObjects(list);
    }

 

AssetDatabase.FindAssets()のフィルタを使えばもっと簡単にできそうだけど、
なぜかうまくいかないのでLINQで対応しています。

 

 

 

まとめ

指定のフォルダにあるPrefabを検索し、ScriptのListに格納するエディター拡張を作りました。
Inspectorを拡張し、格納するボタンを配置しています。
Scriptを編集することで、対象のフォルダや格納するPrefabの条件を設定できます。

 

以上です。

 

 

参考資料

www.urablog.xyz

 

www.hanachiru-blog.com