#概要
備忘録として、UnityEditorの機能であるAdvancedDropdownの使い方を記事にしておきます。
エディター拡張については詳しく説明しません。
今回は、AssetPathを表示、選択できるDropdownを作ります。
#AdvancedDropdownとは
AdvancedDropdownとはUnityEditorの機能で、ゲームオブジェクトのインスペクタに表示されているAddcomponentボタンを押したときに表示されるDropdownと同じものです。
#1.AdvancedDropdownを表示するためのクラスを作成
Sample.csとSampleEditor.csを作成します。
また、SampleEditorはEditor/~に作成します。
public class Sample : MonoBehaviour
{
}
[CustomEditor(typeof(Sample))]
public class SampleEditor : Editor
{
}
#2.AdvancedDropdownを継承したクラスを作成
すべてのアセットパスをDropdownとして表示する機能を作成します。
AdvacnedDropdownを使うには、UnityEditor.IMGUI.Controlsをusingします。
public class AssetPathDropdown : AdvancedDropdown
{
private static readonly float MIN_LINE_COUNT = 15.0f;
public event Action<string> onItemSelected = null;
private Dictionary<int, string> pathDictionary = null;
public AssetPathDropdown(AdvancedDropdownState state) : base(state)
{
pathDictionary = new Dictionary<int, string>();
// 最小サイズを設定
var minimumSize = this.minimumSize;
minimumSize.y = MIN_LINE_COUNT * EditorGUIUtility.singleLineHeight;
this.minimumSize = minimumSize;
}
protected override AdvancedDropdownItem BuildRoot()
{
var root = new AdvancedDropdownItem("AssetPath");
foreach (var path in AssetDatabase.GetAllAssetPaths())
{
var splitStrings = path.Split('/');
var parent = root;
AdvancedDropdownItem lastItem = null;
foreach (var str in splitStrings)
{
var foundChildItem = parent.children.FirstOrDefault(item => item.name == str);
if (foundChildItem != null)
{
// すでに同じ名前のAdvancedDropdownItemがあった場合は次へ
parent = foundChildItem;
lastItem = foundChildItem;
continue;
}
var child = new AdvancedDropdownItem(str);
parent.AddChild(child);
parent = child;
lastItem = child;
}
if (lastItem != null)
{
// KeyをAdvacnedDropdownItemnのidにしてパスを取得できるようにする
pathDictionary[lastItem.id] = path;
}
}
return root;
}
protected override void ItemSelected(AdvancedDropdownItem item)
{
base.ItemSelected(item);
onItemSelected?.Invoke(pathDictionary[item.id]);
}
}
#3.SampleEditor.csを編集
Display dropdown buttonを押したときにDropdownを表示するようにします。そして、Dropdownの要素を選択したときにアセットパスをDebug.Logで出力するようにします。
AdvancedDropdownは内部的に表示されるので、IMGUIですがIMGUIContainerを使用しません。
[CustomEditor(typeof(Sample))]
public class SampleEditor : Editor
{
private static readonly float BUTTON_WIDTH = 225.0f;
private AssetPathDropdown sampleDropdown = null;
public override VisualElement CreateInspectorGUI()
{
var root = new VisualElement();
var button = new Button()
{
text = "Display Dropdown"
};
// ボタンの表示方法を設定
button.style.width = BUTTON_WIDTH;
button.style.alignSelf = Align.Center;
button.style.color = Color.white;
root.Add(button);
sampleDropdown = new AssetPathDropdown(new AdvancedDropdownState());
// イベント購読
sampleDropdown.onItemSelected += OnItemSelected;
button.clicked += () => OnClick(button.worldBound);
return root;
}
private void OnClick(Rect rect)
{
sampleDropdown.Show(rect);
}
private void OnItemSelected(string path)
{
Debug.Log(path);
}
}
##コード
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine.UIElements;
public class AssetPathDropdown : AdvancedDropdown
{
private static readonly float MIN_LINE_COUNT = 15.0f;
public event Action<string> onItemSelected = null;
private Dictionary<int, string> pathDictionary = null;
public AssetPathDropdown(AdvancedDropdownState state) : base(state)
{
pathDictionary = new Dictionary<int, string>();
var minimumSize = this.minimumSize;
minimumSize.y = MIN_LINE_COUNT * EditorGUIUtility.singleLineHeight;
this.minimumSize = minimumSize;
}
protected override AdvancedDropdownItem BuildRoot()
{
var root = new AdvancedDropdownItem("AssetPath");
foreach (var path in AssetDatabase.GetAllAssetPaths())
{
var splitStrings = path.Split('/');
var parent = root;
AdvancedDropdownItem lastItem = null;
foreach (var str in splitStrings)
{
var foundChildItem = parent.children.FirstOrDefault(item => item.name == str);
if (foundChildItem != null)
{
parent = foundChildItem;
lastItem = foundChildItem;
continue;
}
var child = new AdvancedDropdownItem(str);
parent.AddChild(child);
parent = child;
lastItem = child;
}
if (lastItem != null)
{
pathDictionary[lastItem.id] = path;
}
}
return root;
}
protected override void ItemSelected(AdvancedDropdownItem item)
{
base.ItemSelected(item);
onItemSelected?.Invoke(pathDictionary[item.id]);
}
}
[CustomEditor(typeof(Sample))]
public class SampleEditor : Editor
{
private static readonly float BUTTON_WIDTH = 225.0f;
private AssetPathDropdown sampleDropdown = null;
public override VisualElement CreateInspectorGUI()
{
var root = new VisualElement();
var button = new Button()
{
text = "Display Dropdown"
};
button.style.width = BUTTON_WIDTH;
button.style.alignSelf = Align.Center;
button.style.color = Color.white;
root.Add(button);
sampleDropdown = new AssetPathDropdown(new AdvancedDropdownState());
sampleDropdown.onItemSelected += OnItemSelected;
button.clicked += () => OnClick(button.worldBound);
return root;
}
private void OnClick(Rect rect)
{
sampleDropdown.Show(rect);
}
private void OnItemSelected(string path)
{
Debug.Log(path);
}
}
#さいごに
この機能はUnityCsReferenceをみていたら偶然見つけました。あまり記事がないのと使い勝手が良さそうなので、備忘録として残しておきます。