Unity Attribute 自定义属性绘制-绘制脚本属性scriptableObject的面板
为 scriptableObject 在 inspector 的脚本 Field 中 提供了一个自定义的属性检视面板 可以通过这个面板来设置属性的值,而不是在要跳到scriptableObject的面板中去设置
前置知识
- C# Attribute 特性
- Unity Editor 编辑器扩展
- PropertyDrawer 属性绘制器
完整代码
自定义属性代码
using System;
using UnityEngine;
namespace Vanko.Editor.CusProperty
{
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
public class ExpandableAttribute : PropertyAttribute
{
public ExpandableAttribute()
{
}
}
}
属性绘制代码
using System;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using Vanko.Editor.CusProperty;
namespace Vanko.Weapon.Components.Editor
{
[CustomPropertyDrawer(typeof(ExpandableAttribute), true)]
public class ExpandableAttributeDrawer : PropertyDrawer
{
private enum BackgroundStyle
{
None,
HelpBox,
Darken,
Lighten
}
// 是否显示脚本字段
private static bool _showScriptField = true;
// 弹出框的内边距
private static float _innerSpacing = 6.0f;
// 弹出框的外边距
private static float _outerSpacing = 4.0f;
private static BackgroundStyle _backgroundStyle = BackgroundStyle.HelpBox;
private static Color _darkenColor = new(0.0f, 0.0f, 0.0f, 0.2f);
private static Color _lightColor = new(1.0f, 1.0f, 1.0f, 0.2f);
private UnityEditor.Editor editor = null;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
//在默认情况下(未展开),高度为一行.
// singleLineHeight 为unity EditorGUI.TextField or EditorGUI.Popup 的默认一行的高度
float height = EditorGUIUtility.singleLineHeight;
if (property.objectReferenceValue == null) // 引用为空
return height;
if (!property.isExpanded) // 未展开
return height;
if (!editor) // 编辑器为空
// 创建 弹出编辑器
UnityEditor.Editor.CreateCachedEditor(property.objectReferenceValue, null, ref editor);
if (!editor)
return height;
// 获取序列化属性迭代器
SerializedProperty field = editor.serializedObject.GetIterator();
field.NextVisible(true);
if (_showScriptField)
{
height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
}
while (field.NextVisible(false))
{
height += EditorGUI.GetPropertyHeight(field, true) + EditorGUIUtility.standardVerticalSpacing;
}
height += _innerSpacing * 2;
height += _outerSpacing * 2;
return height;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Rect fieldRect = new Rect(position);
fieldRect.height = EditorGUIUtility.singleLineHeight;
UnityEditor.EditorGUI.PropertyField(fieldRect, property, label, true);
if (property.objectReferenceValue == null)
return;
// EditorGUI.indentLevel代表缩进级别,我这里进行-1,是为了让弹出框的箭头前移一格
EditorGUI.indentLevel--;
property.isExpanded = EditorGUI.Foldout(fieldRect, property.isExpanded, GUIContent.none, true);
EditorGUI.indentLevel++;
if (!property.isExpanded)
return;
if (!editor)
UnityEditor.Editor.CreateCachedEditor(property.objectReferenceValue, null, ref editor);
if (!editor)
return;
List<Rect> propertyRects = new List<Rect>();
Rect marginRect = new Rect(fieldRect);
Rect bodyRect = new Rect(fieldRect);
bodyRect.xMin += EditorGUI.indentLevel * 14f;
bodyRect.yMin += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing
+ _outerSpacing;
SerializedProperty field = editor.serializedObject.GetIterator(); // 获取序列化属性
field.NextVisible(true);
marginRect.y += _innerSpacing + _outerSpacing;
if (_showScriptField)
{
marginRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
propertyRects.Add(marginRect);
}
while (field.NextVisible(false))
{
marginRect.y += marginRect.height + EditorGUIUtility.standardVerticalSpacing;
marginRect.height = EditorGUI.GetPropertyHeight(field, true);
propertyRects.Add(marginRect);
}
marginRect.y += _innerSpacing;
bodyRect.yMax = marginRect.yMax;
DrawBackGround(bodyRect);
EditorGUI.indentLevel++;
int index = 0;
field = editor.serializedObject.GetIterator(); // 获取序列化属性迭代器
field.NextVisible(true);
if (_showScriptField)
{
EditorGUI.BeginDisabledGroup(true);
EditorGUI.PropertyField(propertyRects[index], field, true);
EditorGUI.EndDisabledGroup();
index++;
}
while (field.NextVisible(false))
{
try
{
EditorGUI.PropertyField(propertyRects[index], field, true);
}
catch (StackOverflowException)
{
field.objectReferenceValue = null;
}
index++;
}
EditorGUI.indentLevel--;
// 保存对 属性的修改
using (var check = new EditorGUI.ChangeCheckScope())
{
editor.serializedObject.ApplyModifiedProperties();
if (check.changed)
{
property.serializedObject.ApplyModifiedProperties();
}
}
}
private void DrawBackGround(Rect bodyRect)
{
switch (_backgroundStyle)
{
case BackgroundStyle.HelpBox:
EditorGUI.HelpBox(bodyRect, "", MessageType.None);
break;
case BackgroundStyle.Darken:
EditorGUI.DrawRect(bodyRect, _darkenColor);
break;
case BackgroundStyle.Lighten:
EditorGUI.DrawRect(bodyRect, _lightColor);
break;
}
}
}
}
参考教程: