【Unity】ツールバー表示するエディター拡張方法を紹介します!
インスペクターに簡単にツールバー表示できる方法を紹介します。
文字列配列と列挙型に対応します。
Toolbar属性を作成する
文字列配列と列挙型に対応したToolbar属性を実装します。
ToolbarAttribute.cs
using System;
namespace UnityEngine
{
[AttributeUsage(AttributeTargets.Field)]
public class ToolbarAttribute : PropertyAttribute
{
public readonly string[] labels;
public readonly Type type;
public readonly string method;
public ToolbarAttribute(string[] labels, string method = null)
{
this.labels = labels;
type = null;
this.method = method;
}
public ToolbarAttribute(Type type, string method = null)
{
labels = null;
this.type = type;
this.method = method;
}
}
}
文字列配列と列挙型のTypeを取るコンストラクタを2つ用意しました。
method引数は選択されたときに呼び出したいコールバック名です。
nullや空文字の場合は呼び出されません。
文字列配列を使う場合のToolbar属性
[Toolbar(new[] { "foo", "bar", "baz", "qux" })]
public int index;
列挙型を使う場合のToolbar属性
[Toolbar(typeof(Fruit))]
public Fruit fruit;
ツールバーを表示するToolbarDrawerを実装する
ツールバーを表示するToolbarDrawerを実装します。
いくつか注目点をピックアップして説明します。
ツールバーの表示
ツールバーを表示するにはGUI.Toolbar関数を使います。
int newIndex = GUI.Toolbar(position, property.intValue, toolbar.labels);
プロパティータイプの対応
プロパティータイプのint型と列挙型に対応します。
switch(property.propertyType)
{
case SerializedPropertyType.Integer:
DoIntegerToolbar(position, property, label);
break;
case SerializedPropertyType.Enum:
DoEnumToolbar(position, property, label);
break;
default:
break;
}
コールバック関数の呼び出し
リフレクションを利用してコールバック関数を呼び出します。
文字列配列で表示する場合は整数値を、列挙型で表示する場合はその列挙体を引数に渡せるようにします。
void InvokeMethod(SerializedProperty property, object parameter)
{
var toolbar = attribute as ToolbarAttribute;
if(!string.IsNullOrEmpty(toolbar.method))
{
object obj = property.serializedObject.targetObject;
MethodInfo method = obj.GetType().GetMethod(toolbar.method, BindingFlags.Public | BindingFlags.Instance);
method.Invoke(obj, new[] { parameter });
}
}
列挙子のInspectorName属性に対応
列挙子にInspectorName属性で別名を指定されている場合の対応として、
列挙型の各FieldInfoに対してAttribute.GetCustomAttributeでInspectorName属性が付加されている場合に表示名を変えます。
var fields = toolbar.type.GetFields();
foreach (var field in fields)
{
var inspectorName = Attribute.GetCustomAttribute(field, typeof(InspectorNameAttribute)) as InspectorNameAttribute;
if (inspectorName != null)
{
if (result.ContainsKey(field.Name))
{
result[field.Name] = inspectorName.displayName;
}
}
}
ToolbarDrawer.cs
全ソースコードです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace UnityEditor
{
[CustomPropertyDrawer(typeof(ToolbarAttribute))]
public class ToolbarDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
label = EditorGUI.BeginProperty(position, label, property);
switch(property.propertyType)
{
case SerializedPropertyType.Integer:
DoIntegerToolbar(position, property, label);
break;
case SerializedPropertyType.Enum:
DoEnumToolbar(position, property, label);
break;
default:
break;
}
EditorGUI.EndProperty();
}
void DoIntegerToolbar(Rect position, SerializedProperty property, GUIContent label)
{
var toolbar = attribute as ToolbarAttribute;
EditorGUI.BeginChangeCheck();
int newIndex = GUI.Toolbar(position, property.intValue, toolbar.labels);
if (EditorGUI.EndChangeCheck())
{
property.intValue = newIndex;
InvokeMethod(property, newIndex);
}
}
void DoEnumToolbar(Rect position, SerializedProperty property, GUIContent label)
{
var toolbar = attribute as ToolbarAttribute;
EditorGUI.BeginChangeCheck();
int newIndex = GUI.Toolbar(position, property.enumValueIndex, GetInspectorNames());
if (EditorGUI.EndChangeCheck())
{
property.enumValueIndex = newIndex;
InvokeMethod(property, Enum.GetValues(toolbar.type).GetValue(newIndex));
}
}
void InvokeMethod(SerializedProperty property, object parameter)
{
var toolbar = attribute as ToolbarAttribute;
if(!string.IsNullOrEmpty(toolbar.method))
{
object obj = property.serializedObject.targetObject;
MethodInfo method = obj.GetType().GetMethod(toolbar.method, BindingFlags.Public | BindingFlags.Instance);
method.Invoke(obj, new[] { parameter });
}
}
string[] GetInspectorNames()
{
var toolbar = attribute as ToolbarAttribute;
Dictionary<string, string> result = new Dictionary<string, string>();
var names = toolbar.type.GetEnumNames();
foreach (var name in names)
{
result.Add(name, name);
}
var fields = toolbar.type.GetFields();
foreach (var field in fields)
{
var inspectorName = Attribute.GetCustomAttribute(field, typeof(InspectorNameAttribute)) as InspectorNameAttribute;
if (inspectorName != null)
{
if (result.ContainsKey(field.Name))
{
result[field.Name] = inspectorName.displayName;
}
}
}
return result.Values.ToArray();
}
}
}
文字列配列ツールバーの使い方
文字列配列を使ったツールバー表示のテストスクリプトです。
コールバック関数の引数はint型の選択されたインデックスが渡されます。
using UnityEngine;
public class TestScript : MonoBehaviour
{
[Toolbar(new[] { "foo", "bar", "baz", "qux" }, "ItemSelected")]
public int index;
public void ItemSelected(int newIndex)
{
Debug.Log(newIndex + "番目のアイテムが選択されました");
}
}
ツールバーボタンを押すとログが出力されます。
列挙型ツールバーの使い方
列挙型を使ったツールバー表示のテストスクリプトです。
コールバック関数の引数は選択された列挙子が渡されます。
using UnityEngine;
public class TestScript : MonoBehaviour
{
public enum Fruit
{
[InspectorName("りんご")]
Apple,
[InspectorName("ばなな")]
Banana,
[InspectorName("みかん")]
Orange,
}
[Toolbar(typeof(Fruit), "FruitSelected")]
public Fruit fruit;
public void FruitSelected(Fruit newFruit)
{
Debug.Log(newFruit + "が選択されました");
}
}
列挙アイテムを選択するとログが出力されます。
列挙型は標準でコンボボックスで表示され選択しているメンバー以外は見えなくなります。
メンバーが少ない列挙型の場合はツールバー表示すると分かりやすいですね。
関連ページ
こちらのページも合わせてご覧下さい。