You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity多PropertyDrawer兼容异常求助:IconBool与LockInInspector共存失效

Unity多自定义PropertyDrawer冲突问题修复

问题根源

Unity对同一字段的多个PropertyDrawer采用链式执行逻辑,当前代码的两个问题:

  1. IconBool在前、Lock在后:IconBoolDrawer完全自定义绘制Toggle,未调用EditorGUI.PropertyField触发后续LockInInspectorDrawer,导致禁用逻辑根本不执行。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 14:45:32