Unity多PropertyDrawer兼容异常求助:IconBool与LockInInspector共存失效
Unity多自定义PropertyDrawer冲突问题修复
问题根源
Unity对同一字段的多个PropertyDrawer采用链式执行逻辑,当前代码的两个问题:
- IconBool在前、Lock在后:
IconBoolDrawer完全自定义绘制Toggle,未调用EditorGUI.PropertyField触发后续LockInInspectorDrawer,导致禁用逻辑根本不执行。 - Lock在前、IconBool在后:
LockInInspectorDrawer未重写GetPropertyHeight,返回默认行高,作为链式首节点,Inspector会忽略IconBoolDrawer的自定义高度。
修复方案
1. 修复LockInInspectorDrawer
重写高度逻辑让后续Drawer生效,使用EditorGUI.DisabledScope替代全局GUI.enabled,支持安全嵌套:
[CustomPropertyDrawer(typeof(LockInInspectorAttribute))] public class LockInInspectorDrawer : PropertyDrawer { // 让后续Drawer的高度决定最终显示高度 public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { return base.GetPropertyHeight(property, label); } public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { // 使用作用域类实现安全禁用,避免全局状态污染 using (new EditorGUI.DisabledScope(true)) { // 触发后续Drawer的绘制流程 EditorGUI.PropertyField(position, property, label); } } }
2. 修复IconBoolDrawer
尊重当前GUI启用状态,调用EditorGUI.PropertyField传递链式流程,仅在启用时允许修改属性:
[CustomPropertyDrawer(typeof(IconBoolAttribute))] public class IconBoolDrawer : PropertyDrawer { IconBoolAttribute iconBool; readonly float lineHeightScaler = 1.5f; bool setted; Color red; Color green; Texture2D icon; GUIContent content; Vector2 contentSize; public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { return EditorGUIUtility.singleLineHeight * lineHeightScaler; } public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { if (property.propertyType != SerializedPropertyType.Boolean) { Debug.LogError("Property with [IconBool] attribute must be type of 'bool'"); base.OnGUI(position, property, label); return; } if (!setted) { iconBool = attribute as IconBoolAttribute; ColorUtility.TryParseHtmlString("#FF7585", out red); ColorUtility.TryParseHtmlString("#90EEC9", out green); icon = EditorGUIUtility.Load(iconBool.iconPath) as Texture2D; content = new GUIContent(label.text); contentSize = EditorStyles.label.CalcSize(content); setted = true; } EditorGUI.BeginProperty(position, label, property); // 保存当前GUI启用状态,控制是否允许修改属性 bool isEnabled = GUI.enabled; // 绘制自定义图标 GUI.color = property.boolValue ? green : red; Rect iconRect = new Rect(position.x, position.y, position.height, position.height); GUI.DrawTexture(iconRect, icon); GUI.color = Color.white; // 绘制文本(如果启用) if (iconBool.hasText) { Rect textRect = new Rect( iconRect.xMax + 5, position.y + (position.height - EditorGUIUtility.singleLineHeight), contentSize.x, EditorGUIUtility.singleLineHeight ); GUI.Label(textRect, label.text, EditorStyles.label); } // 绘制自定义Toggle,仅在GUI启用时允许修改 Rect toggleRect = new Rect( EditorGUIUtility.labelWidth - ((EditorGUI.indentLevel - 1) * 15) + 5, position.y, position.width - (EditorGUI.labelWidth - ((EditorGUI.indentLevel - 1) * 15) + 5), position.height ); EditorGUI.BeginChangeCheck(); bool newValue = GUI.Toggle(toggleRect, property.boolValue, "", EditorStyles.toggle); if (EditorGUI.EndChangeCheck() && isEnabled) { property.boolValue = newValue; } // 触发后续Drawer逻辑,用空标签隐藏默认控件避免重复绘制 EditorGUI.PropertyField(position, property, GUIContent.none); EditorGUI.EndProperty(); } void DrawRect(Rect rect) { Vector3 tl = new Vector3(rect.xMin, rect.yMin, 0); Vector3 tr = new Vector3(rect.xMax, rect.yMin, 0); Vector3 bl = new Vector3(rect.xMin, rect.yMax, 0); Vector3 br = new Vector3(rect.xMax, rect.yMax, 0); Handles.DrawLine(tl, tr); Handles.DrawLine(bl, br); Handles.DrawLine(tl, bl); Handles.DrawLine(tr, br); } }
修复效果说明
- Lock在前时:Lock的
GetPropertyHeight会返回IconBool的自定义高度,Inspector分配正确区域;DisabledScope禁用GUI后,IconBool的Toggle会尊重该状态,无法修改。 - IconBool在前时:IconBool调用
EditorGUI.PropertyField触发Lock的禁用逻辑,GUI状态被正确设置,同时高度由IconBool控制。 - 扩展性:后续新增
PropertyDrawer时,只要遵循「重写GetPropertyHeight返回base值」「使用作用域类控制GUI状态」「调用EditorGUI.PropertyField传递流程」的规则,即可正常嵌套生效。
内容的提问来源于stack exchange,提问作者SoMuch SMx
相关产品推荐
相关产品推荐

