using UnityEngine; using UnityEditor; using System; /// <summary> /// PropertyDrawerで、1行に収めるための基底クラス /// </summary> public class OneLineDrawer : PropertyDrawer { /// <summary>描画領域全体</summary> private Rect wholeRect; /// <summary>X座標の部分和</summary> private float partialSum; /// <summary>自身のプロパティ</summary> private SerializedProperty prop; /// <summary>インデント段階を保存</summary> private int lastIndentLevel; private int pastLabelWidth; /// <summary> /// プロパティの開始 /// </summary> /// <param name="position">OnGUIと同じ</param> /// <param name="property">OnGUIと同じ</param> /// <param name="label">OnGUIと同じ</param> protected void BeginProperty(Rect position, SerializedProperty property, GUIContent label) { pastLabelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 80 + EditorGUI.indentLevel * 20; label = EditorGUI.BeginProperty(position, label, property); Init(position, property, label); this.lastIndentLevel = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; } /// <summary> /// プロパティの終了 /// </summary> protected void EndProperty() { EditorGUI.indentLevel = this.lastIndentLevel; EditorGUI.EndProperty(); } /// <summary> /// 初期化 /// </summary> /// <param name="position">OnGUIと同じ</param> /// <param name="property">OnGUIと同じ</param> /// <param name="label">OnGUIと同じ</param> void Init(Rect position, SerializedProperty property, GUIContent label) { this.partialSum = 0; this.prop = property; this.wholeRect = EditorGUI.PrefixLabel(position, label); this.wholeRect.x += pastLabelWidth - EditorGUIUtility.labelWidth; this.wholeRect.width -= pastLabelWidth - EditorGUIUtility.labelWidth; } /// <summary> /// 1行の中での描画の指定 /// </summary> /// <param name="widthRate">全体に占めるプロパティの割合</param> /// <param name="propertyName">プロパティの名前</param> /// <param name="label">ラベルに使用する文字列</param> /// <param name="labelWidth">ラベルの幅</param> protected void DividedField(float widthRate, string propertyName, string label = "", float labelWidth = 0) { Debug.Assert(0 < widthRate); Debug.Assert(widthRate < 1); Debug.Assert(!string.IsNullOrEmpty(propertyName)); Debug.Assert(label != null); Debug.Assert(labelWidth >= 0); // 幅とRectの算出 var width = this.wholeRect.width * widthRate; var rect = new Rect(this.wholeRect.x + this.partialSum, this.wholeRect.y, width, this.wholeRect.height); this.partialSum += width; // プロパティが見えなくならない様に調整 labelWidth = Mathf.Clamp(labelWidth, 0, rect.width - 20); // ラベル幅を設定し、プロパティを描画する EditorGUIUtility.labelWidth = labelWidth; var item = this.prop.FindPropertyRelative(propertyName); if (item == null) { Debug.LogWarningFormat("Failed to find property: '{0}' in '{1}'", propertyName, this.GetType()); } else { EditorGUI.PropertyField(rect, item, new GUIContent(label)); } } /// <summary> /// BeginProperty()とEndProperty()を自動で呼ぶラッパークラス /// </summary> protected class PropertyWrapper : IDisposable { /// <summary>元クラスの参照</summary> OneLineDrawer self; /// <summary> /// ctor /// </summary> /// <param name="self">自身</param> /// <param name="position">OnGUIと同じ</param> /// <param name="property">OnGUIと同じ</param> /// <param name="label">OnGUIと同じ</param> public PropertyWrapper(OneLineDrawer self, Rect position, SerializedProperty property, GUIContent label) { this.self = self; self.BeginProperty(position, property, label); } /// <summary> /// IDisposableの実装 /// </summary> public void Dispose() { self.EndProperty(); } } }
使い方:
using UnityEngine; using UnityEditor; [CustomPropertyDrawer(typeof(SomeClass))] public class SomeClassDrawer : OneLineDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { using (new PropertyWrapper(this, position, property, label)) { DividedField(0.1f, "active"); DividedField(0.15f, "stage"); DividedField(0.15f, "level", "-", 10); DividedField(0.3f, "hp", "hp", 44); DividedField(0.3f, "gold", "gold", 44); } } }
これです、これがやりたかった。最終的なドロアークラスの記述が素晴らしく簡潔になりました。
OneLineDrawer
自身はOnGUI()
を実装せず、関数とラッパークラスを用意し、それを継承してラッパークラスをインスタンス化して、メソッドの呼び出しをラッパークラスに任せて使用します。
自分で呼ぶと呼び忘れがありますからね。
とりあえず満足しました。