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

Unity自定义PropertyDrawer中枚举控件与滑块重叠问题求助

Unity自定义PropertyDrawer中枚举控件与滑块重叠问题求助

大家好,我最近在做Unity编辑器扩展的时候遇到个头疼的问题:我自定义了GapRangeAttribute和对应的GapRangeDrawer,用来在Inspector里给float变量显示可切换整数/浮点数模式的滑块。但现在的问题是,这个Drawer里的枚举选择框和滑块会和脚本里的其他变量重叠(就是红圈标注的部分)。我尝试过写一个自定义的CubeSpawnerEditor来手动布局,但效果没改善多少,最后又回到了原来的代码。

下面是我目前的代码和尝试过的方案:

1. 属性定义脚本(GapRangeAttribute)

using UnityEngine;
using UnityEditor;

public enum GapDisplayMode
{
    Integers,
    Floats
}

public class GapRangeAttribute : PropertyAttribute
{
    public float minRange;
    public float maxRange;
    public GapDisplayMode displayMode;

    public GapRangeAttribute(float minRange, float maxRange, GapDisplayMode displayMode)
    {
        this.minRange = minRange;
        this.maxRange = maxRange;
        this.displayMode = displayMode;
    }
}

2. PropertyDrawer脚本(GapRangeDrawer)

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(GapRangeAttribute))]
public class GapRangeDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        GapRangeAttribute gapRange = attribute as GapRangeAttribute;

        if (property.propertyType == SerializedPropertyType.Float)
        {
            EditorGUI.BeginProperty(position, label, property);

            Rect popupPosition = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
            Rect sliderPosition = new Rect(position.x, position.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing, position.width, EditorGUIUtility.singleLineHeight);

            gapRange.displayMode = (GapDisplayMode)EditorGUI.EnumPopup(popupPosition, "Display Mode", gapRange.displayMode);

            if (gapRange.displayMode == GapDisplayMode.Integers)
            {
                int intValue = EditorGUI.IntSlider(sliderPosition, label, Mathf.RoundToInt(property.floatValue), (int)gapRange.minRange, (int)gapRange.maxRange);
                property.floatValue = intValue;
            }
            else
            {
                float floatValue = EditorGUI.Slider(sliderPosition, label, property.floatValue, gapRange.minRange, gapRange.maxRange);
                property.floatValue = floatValue;
            }

            EditorGUI.EndProperty();
        }
        else
        {
            EditorGUI.LabelField(position, label.text, "Use [GapRangeDrawer] with float values only!");
        }
    }
}

3. 测试用MonoBehaviour脚本(CubeSpawner)

using UnityEngine;
using UnityEditor;

public class CubeSpawner : MonoBehaviour
{
    public GameObject cubePrefab;
    public LayerMask terrainLayer;

    [GapRange(0, 100, GapDisplayMode.Floats)]
    public float gap = 0f;

    // 还有其他变量比如cameraMoveSpeed、zoomSpeed等
}

我尝试过的替代方案(自定义Editor)

我还写了一个CubeSpawnerEditor手动控制布局,但问题依然存在,最后又回到了Drawer方案:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(CubeSpawner))]
public class CubeSpawnerEditor : Editor
{
    private SerializedProperty gapProperty;

    private void OnEnable()
    {
        gapProperty = serializedObject.FindProperty("gap");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        CubeSpawner cubeSpawner = (CubeSpawner)target;

        EditorGUILayout.PropertyField(serializedObject.FindProperty("cubePrefab"));
        EditorGUILayout.PropertyField(serializedObject.FindProperty("terrainLayer"));

        GapDisplayMode displayMode = cubeSpawner.displayMode;
        displayMode = (GapDisplayMode)EditorGUILayout.EnumPopup("Display Mode", displayMode);

        EditorGUILayout.Space();

        if (displayMode == GapDisplayMode.Floats)
        {
            EditorGUILayout.Slider(gapProperty, cubeSpawner.minFloatRange, cubeSpawner.maxFloatRange, "Gap");
        }
        else if (displayMode == GapDisplayMode.Integers)
        {
            EditorGUILayout.IntSlider(gapProperty, (int)cubeSpawner.minIntRange, (int)cubeSpawner.maxIntRange, "Gap");
        }

        EditorGUILayout.PropertyField(serializedObject.FindProperty("cameraMoveSpeed"));
        EditorGUILayout.PropertyField(serializedObject.FindProperty("zoomSpeed"));
        EditorGUILayout.PropertyField(serializedObject.FindProperty("rotationSpeed"));

        serializedObject.ApplyModifiedProperties();
    }
}

问题核心

现在的关键问题是:自定义的PropertyDrawer里我绘制了两行UI(枚举选择框+滑块),但Unity编辑器默认只给这个属性分配了一行的高度空间,导致下面的其他控件往上挤,和滑块重叠了。


解决方法:重写GetPropertyHeight方法

要解决重叠问题,我们需要告诉Unity编辑器这个属性需要占用多少高度。只需要在GapRangeDrawer里重写GetPropertyHeight方法,返回两行控件的高度加上间距即可:

修改后的完整GapRangeDrawer脚本:

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(GapRangeAttribute))]
public class GapRangeDrawer : PropertyDrawer
{
    // 重写方法,告诉编辑器该属性需要的垂直高度
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        // 两行控件高度 + 标准垂直间距
        return EditorGUIUtility.singleLineHeight * 2 + EditorGUIUtility.standardVerticalSpacing;
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        GapRangeAttribute gapRange = attribute as GapRangeAttribute;

        if (property.propertyType == SerializedPropertyType.Float)
        {
            EditorGUI.BeginProperty(position, label, property);

            Rect popupPosition = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
            Rect sliderPosition = new Rect(position.x, position.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing, position.width, EditorGUIUtility.singleLineHeight);

            gapRange.displayMode = (GapDisplayMode)EditorGUI.EnumPopup(popupPosition, "Display Mode", gapRange.displayMode);

            if (gapRange.displayMode == GapDisplayMode.Integers)
            {
                int intValue = EditorGUI.IntSlider(sliderPosition, label, Mathf.RoundToInt(property.floatValue), (int)gapRange.minRange, (int)gapRange.maxRange);
                property.floatValue = intValue;
            }
            else
            {
                float floatValue = EditorGUI.Slider(sliderPosition, label, property.floatValue, gapRange.minRange, gapRange.maxRange);
                property.floatValue = floatValue;
            }

            EditorGUI.EndProperty();
        }
        else
        {
            EditorGUI.LabelField(position, label.text, "Use [GapRangeDrawer] with float values only!");
            // 错误情况返回默认单行高度
            base.GetPropertyHeight(property, label);
        }
    }
}

原理说明

Unity的PropertyDrawer默认会给每个属性分配一行的高度(EditorGUIUtility.singleLineHeight)。当我们在OnGUI里绘制了多行控件时,必须通过GetPropertyHeight主动告诉编辑器这个属性需要更多的垂直空间,这样编辑器才会给这个属性留出足够的位置,不会让下面的控件往上重叠。

如果用自定义Editor的方式,EditorGUILayout会自动处理高度,但Drawer方案更通用,适合多个脚本复用这个属性。

备注:内容来源于stack exchange,提问作者Daniel Lip

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 11:18:03