トップ > 【Unity】エディター拡張方法をまとめます! > 【Unity】ツールバー表示するエディター拡張方法を紹介します!
更新日 2022/7/14

【Unity】ツールバー表示するエディター拡張方法を紹介します!

インスペクターに簡単にツールバー表示できる方法を紹介します。
文字列配列と列挙型に対応します。

ToolbarDrawer ToolbarDrawer_Enum

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 + "番目のアイテムが選択されました");
}
}
ToolbarDrawer

ツールバーボタンを押すとログが出力されます。

ToolbarDrawer_Log

列挙型ツールバーの使い方

列挙型を使ったツールバー表示のテストスクリプトです。
コールバック関数の引数は選択された列挙子が渡されます。

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 + "が選択されました");
}
}
ToolbarDrawer_Enum

列挙アイテムを選択するとログが出力されます。

ToolbarDrawer_EnumLog

列挙型は標準でコンボボックスで表示され選択しているメンバー以外は見えなくなります。
メンバーが少ない列挙型の場合はツールバー表示すると分かりやすいですね。


関連ページ


Copyright ©2022 - 2024 うにぉらぼ