2
\$\begingroup\$

This is a bit of an interesting one. Read about custom Property Drawers in the Unity Docs. Read about MinMaxSlider in the Unity Docs.

Unity's EditorGUI tools used for building property drawers expose a default MinMaxSlider, though the standard Unity libraries do not provide a way to use that drawer without custom code. This aims to be a reusable generally-applicable way to use a MinMaxSlider where it would be most commonly appropriate: defining a range to clamp some value at run-time or to generate a random value from within that range.

I chose to follow MinMaxSlider for naming, so I have struct MinMaxPair, class MinMaxAttribute : PropertyAttribute, and class MinMaxDrawer : PropertyDrawer. I also chose to follow the C# naming conventions default to ReSharper rather than Unity's: private _camelCase, local camelCase, public TitleCase.

To use this, you put a serialized field on a MonoBehaviour (or SriptableObject) like so:

[SerializeField] [MinMax(1, 10)] private MinMaxPair _orthographicSizeRange;

And it shows in the editor like this:

MinMaxDrawer example

Each of the semicircles are individually grabbable to move the ends of the range and the bar is grabbable to move the whole range without changing the size. Values can be entered into the fields as well, which clamp to the outside of the allowed range and to the other value. If a MinMaxAttribute is not provided, the drawer defaults to be in the range [0,1]. We hide the MinMaxPair property when in multi-edit mode, as otherwise data loss would happen because this method of custom properties does not support multi-editing.

MinMaxPair.cs (in Assembly-CSharp)

public class MinMaxAttribute : PropertyAttribute
{
    public float Min, Max;

    public MinMaxAttribute(float min, float max)
    {
        Min = min;
        Max = max;
    }
}

[Serializable]
public struct MinMaxPair
{
    public float Min, Max;

    public MinMaxPair(float min, float max)
    {
        Min = min;
        Max = max;
    }

    public float Clamp(float value)
    {
        return Mathf.Clamp(value, Min, Max);
    }

    public float RandomValue
    {
        get { return UnityEngine.Random.Range(Min, Max); }
    }
}

MinMaxDrawer.cs (in Assembly-CSharp-Editor)

[CustomPropertyDrawer(typeof(MinMaxPair))]
[CustomPropertyDrawer(typeof(MinMaxAttribute))]
public class MinMaxDrawer : PropertyDrawer
{
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        if (property.serializedObject.isEditingMultipleObjects) return 0f;
        return base.GetPropertyHeight(property, label) + 16f;
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        if (property.serializedObject.isEditingMultipleObjects) return;

        var minProperty = property.FindPropertyRelative("Min");
        var maxProperty = property.FindPropertyRelative("Max");
        var minmax = attribute as MinMaxAttribute ?? new MinMaxAttribute(0, 1);
        position.height -= 16f;

        label = EditorGUI.BeginProperty(position, label, property);
        position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
        var min = minProperty.floatValue;
        var max = maxProperty.floatValue;

        var left = new Rect(position.x, position.y, position.width / 2 - 11f, position.height);
        var right = new Rect(position.x + position.width - left.width, position.y, left.width, position.height);
        var mid = new Rect(left.xMax, position.y, 22, position.height);
        min = Mathf.Clamp(EditorGUI.FloatField(left, min), minmax.Min, max);
        EditorGUI.LabelField(mid, " to ");
        max = Mathf.Clamp(EditorGUI.FloatField(right, max), min, minmax.Max);

        position.y += 16f;
        EditorGUI.MinMaxSlider(position, GUIContent.none, ref min, ref max, minmax.Min, minmax.Max);

        minProperty.floatValue = min;
        maxProperty.floatValue = max;
        EditorGUI.EndProperty();
    }
}
\$\endgroup\$
  • \$\begingroup\$ Do you know a way to avoid using the FindPropertyRelative and searching by name as a string? This seems very non-object oriented to me and seems to be impossible for long-term maintenance \$\endgroup\$ – Adam B Sep 18 '18 at 16:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.