Search Unity

Inspector 2 Variables in one Slider

Discussion in 'Scripting' started by MysteriX, Jun 25, 2014.

  1. MysteriX

    MysteriX

    Joined:
    Apr 8, 2014
    Posts:
    54
    Hey,
    today I noticed you can write something like this:

    Code (csharp):
    1.  
    2. [Range(1,10)]
    3. public float minRangeOfCamera = 2.0f;
    4. [Range(1,10)]
    5. public float maxRangeOfCamera = 20.0f;
    6.  
    in the Inspector there will be a slider to change the values.
    But I often saw sliders thich contained 2 slider in it. So there I was able to change the min and the max value in just one slider. Can you tell me how to do it?

    Thanks
     
  2. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
  3. MysteriX

    MysteriX

    Joined:
    Apr 8, 2014
    Posts:
    54
    There is no way without spending money? :/
     
  4. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Yes, the first option I gave you... Writing a custom editor/property drawer yourself. It's really not that hard.
     
  5. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
  6. MysteriX

    MysteriX

    Joined:
    Apr 8, 2014
    Posts:
    54
  7. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
  8. smetzzz

    smetzzz

    Joined:
    Mar 24, 2014
    Posts:
    145
    I have attempted to write a MinMaxSlider property drawer but failed. Is this possible? I can't seem to get around this error
    No appropriate version of 'UnityEditor.EditorGUILayout.MinMaxSlider' for the argument list
    I really wish a MinMax slider was a simple attribute like Range.
     
    ModLunar likes this.
  9. TommySKD

    TommySKD

    Joined:
    Jul 23, 2014
    Posts:
    25
    Hello here's what I got just wanna share

    It works only with Vector2/Vector2Int!
    So the X component represents the from/min and the Y component represents the to/max.
    The value to the left of the slider is the X, the value to the right is the Y

    These tests all have the same Vector2 values: (x=1, y=2)
    It is just the way the MinTo attribute is used that changes what the slider represents in the inspector.
    MinTo(): from x to y, between 0 and y.
    MinTo(max): from x to y, between 0 and max.
    MinTo(max, min): from x to y, between min and max.

    I use it for things like a spell min/max distance or like a ranged dude AI distance not too far not too close.
    Also for durations, like if you can charge an attack for up to Y second but also release it early after X seconds. So most of the time I just wanna adjust freely the maximum/to and the minimum is always 0 because negative values don't make sense for these uses, so yea just gives you little fun controls to toy with, also nice way to see how big the range is and where it starts, and also it proofchecks that -you don't put dumb negative values -your minimum does not exceed the maximum.
    I decided to call it MinTo instead of MinMax because most of the time the To is the Max (with zero args) so there's no Max actually. For example in test1 there's 2 on the right as the "max" but I can change it to 100000 from the inspector if I want so yea it is more a just a To value than a Maximum by definition.

    MinToAttribute.cs: put this script anywhere
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class MinToAttribute : PropertyAttribute
    5. {
    6.     public float? max;
    7.     public float min;
    8.  
    9.     public MinToAttribute() {}
    10.     public MinToAttribute(float max, float min = 0)
    11.     {
    12.         this.max = max;
    13.         this.min = min;
    14.     }
    15. }

    MinToDrawer.cs: and put this one in an Editor folder!
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CustomPropertyDrawer(typeof(MinToAttribute))]
    5. public class MinToDrawer : PropertyDrawer
    6. {
    7.     public override void OnGUI(Rect position,
    8.                                SerializedProperty property,
    9.                                GUIContent label)
    10.     {
    11.         var ctrlRect = EditorGUI.PrefixLabel(position, label);
    12.         Rect[] r = Geom.SplitRectIn3(ctrlRect, 36, 5);
    13.         var att = (MinToAttribute)attribute;
    14.         var type = property.propertyType;
    15.         if (type == SerializedPropertyType.Vector2)
    16.         {
    17.             var vec = property.vector2Value;
    18.             float min = vec.x;
    19.             float to = vec.y;
    20.             min = EditorGUI.FloatField(r[0], min);
    21.             to = EditorGUI.FloatField(r[2], to);
    22.             EditorGUI.MinMaxSlider(r[1], ref min, ref to, att.min, att.max ?? to);
    23.             vec = new Vector2(min < to ? min : to, to);
    24.             property.vector2Value = vec;
    25.         }
    26.         else if (type == SerializedPropertyType.Vector2Int)
    27.         {
    28.             var vec = property.vector2IntValue;
    29.             float min = vec.x;
    30.             float to = vec.y;
    31.             min = EditorGUI.IntField(r[0], (int)min);
    32.             to = EditorGUI.IntField(r[2], (int)to);
    33.             EditorGUI.MinMaxSlider(r[1], ref min, ref to, att.min, att.max ?? to);
    34.             vec = new Vector2Int(Mathf.RoundToInt(min < to ? min : to), Mathf.RoundToInt(to));
    35.             property.vector2IntValue = vec;
    36.         }
    37.         else
    38.             EditorGUI.HelpBox(ctrlRect, "MinTo is for Vector2!!", MessageType.Error);
    39.     }
    40.  
    41.     public static Rect[] SplitRectIn3(Rect rect, int bordersSize, int space = 0)
    42.     {
    43.         var r = SplitRect(rect, 3);
    44.         int pad = (int)r[0].width - bordersSize;
    45.         int ps = pad + space;
    46.         r[0].width = r[2].width -= ps;
    47.         r[1].width += pad * 2;
    48.         r[1].x -= pad;
    49.         r[2].x += ps;
    50.         return r;
    51.     }
    52.     public static Rect[] SplitRect(Rect a, int n)
    53.     {
    54.         Rect[] r = new Rect[n];
    55.         for (int i = 0; i < n; ++i)
    56.             r[i] = new Rect(a.x + a.width / n * i, a.y, a.width / n, a.height);
    57.         return r;
    58.     }
    59. }
     
    Last edited: Oct 18, 2018
  10. TommySKD

    TommySKD

    Joined:
    Jul 23, 2014
    Posts:
    25
    quick version 2 LAST ONE :p of course you can look at the code to see how I did it and to tweak it how you want.

    FLOAT
    -You can do it with 2 separate float variables now instead of Vector2! That's cool if you change your mind and just wanna remove the min and just keep the max or the opposite whatever it is less of a commitment then having to use Vector2.
    So you just put the thing above the MAX/TO float, then you can let the autoname template get the MIN or specify it. (It is relative to you so if you're a nested class or struct you don't have to write "spell.distanceMin" if max is in spell already you just write "distanceMin")
    I didn't show all the combinations but you can do the same arguments as above for different behaviors.
    (oh also I changed signature for test3(3,1) it wasn't very intuitive now it is written test3(1,3) so it is MinTo(min, max))


    NASTY BUG FIXED
    -If you selected multiple objects it would override values from one of them on MinTo even if you didn't do anything, now you can still override for all at once but only if you start tweaking it, just like unity.

    *It is using ".NET 4.x Equivalent" if it doesn't compile that's most likely because you didn't enable it you do that in Edit/Project Settings/Player

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. // Can use it on:
    6. // VECTOR2 / VECTOR2INT
    7. // FLOAT
    8. // you put the attribute above/before the max value!
    9. // then you specify in the MinTo arguments (if the minName template doesn't work)
    10. // the name of the min float variable
    11. public class MinToAttribute : PropertyAttribute
    12. {
    13.     // $ becomes the name of the max property
    14.     // example: [MinTo] float duration; float durationMin;
    15.     public string minName = "$Min";
    16.     public float? max;
    17.     public float min;
    18.  
    19.     public MinToAttribute(string minName = null)
    20.     {
    21.         if (minName != null)
    22.             this.minName = minName;
    23.     }
    24.     public MinToAttribute(float max, string minName = null) : this(0, max, minName) { }
    25.     public MinToAttribute(float min, float max, string minName = null) : this(minName)
    26.     {
    27.         this.max = max;
    28.         this.min = min;
    29.     }
    30. }
    Code (CSharp):
    1.  
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. [CustomPropertyDrawer(typeof(MinToAttribute))]
    6. public class MinToDrawer : PropertyDrawer
    7. {
    8.     public override void OnGUI(Rect position,
    9.                                SerializedProperty property,
    10.                                GUIContent label)
    11.     {
    12.         position.height = EditorGUIUtility.singleLineHeight;
    13.         var att = (MinToAttribute)attribute;
    14.         var type = property.propertyType;
    15.  
    16.         string minName = att.minName.Replace("$", property.name);
    17.         int lastDot = property.propertyPath.LastIndexOf('.');
    18.         if (lastDot > -1)
    19.             minName = property.propertyPath.Substring(0, lastDot) + '.' + minName;
    20.         //Debug.Log("minName=" + minName);
    21.  
    22.         if (type == SerializedPropertyType.Float)
    23.             //label.text = string.Format("({1}-{0})", property.name, minName);
    24.             label.text = " ";
    25.         var ctrlRect = EditorGUI.PrefixLabel(position, label);
    26.         Rect[] r = SplitRectIn3(ctrlRect, 36, 5);
    27.         if (type == SerializedPropertyType.Vector2)
    28.         {
    29.             EditorGUI.BeginChangeCheck();
    30.             var vec = property.vector2Value;
    31.             float min = vec.x;
    32.             float to = vec.y;
    33.             min = EditorGUI.FloatField(r[0], min);
    34.             to = EditorGUI.FloatField(r[2], to);
    35.             EditorGUI.MinMaxSlider(r[1], ref min, ref to, att.min, att.max ?? to);
    36.             vec = new Vector2(min < to ? min : to, to);
    37.             if (EditorGUI.EndChangeCheck())
    38.                 property.vector2Value = vec;
    39.         }
    40.         else if (type == SerializedPropertyType.Vector2Int)
    41.         {
    42.             EditorGUI.BeginChangeCheck();
    43.             var vec = property.vector2IntValue;
    44.             float min = vec.x;
    45.             float to = vec.y;
    46.             min = EditorGUI.IntField(r[0], (int)min);
    47.             to = EditorGUI.IntField(r[2], (int)to);
    48.             EditorGUI.MinMaxSlider(r[1], ref min, ref to, att.min, att.max ?? to);
    49.             vec = new Vector2Int(Mathf.RoundToInt(min < to ? min : to), Mathf.RoundToInt(to));
    50.             if (EditorGUI.EndChangeCheck())
    51.                 property.vector2IntValue = vec;
    52.         }
    53.         else if (type == SerializedPropertyType.Float)
    54.         {
    55.             EditorGUI.BeginChangeCheck();
    56.             // Line setup
    57.             var line2 = position;
    58.             line2.y += EditorGUIUtility.singleLineHeight;
    59.  
    60.             // Swap lines
    61.             // Comment these 3 lines if you want the slider above
    62.             // Or uncomment them if you want the slider sandwiched between min and max
    63.             //var y = line2.y;
    64.             //line2.y = r[0].y;
    65.             //r[0].y = r[1].y = r[2].y = y;
    66.  
    67.             // First we draw the float below/above as normal
    68.             EditorGUI.PropertyField(line2, property);
    69.  
    70.             // Then the slider
    71.             var minProperty = property.serializedObject.FindProperty(minName);
    72.             if (minProperty?.propertyType != SerializedPropertyType.Float)
    73.             {
    74.                 EditorGUI.HelpBox(ctrlRect, "Min float not found!!", MessageType.Info);
    75.                 return;
    76.             }
    77.             float minVal = minProperty.floatValue;
    78.             float maxVal = property.floatValue;
    79.  
    80.             EditorGUI.MinMaxSlider(r[1], ref minVal, ref maxVal, att.min, att.max ?? maxVal);
    81.             EditorGUI.LabelField(r[0], att.min.ToString());
    82.  
    83.             if (att.max.HasValue && maxVal > att.max.Value)
    84.             {
    85.                 // Shows that the max value overflowed the slider
    86.                 // So if you just wanna try infinite range and stuff you just put 999
    87.                 // and it shows clearly that it is a big test value with the color
    88.                 // This is only if you specify a max value in the attribute
    89.                 Color c = GUI.contentColor;
    90.                 GUI.contentColor = overflowColor;
    91.                 EditorGUI.LabelField(r[2], maxVal.ToString());
    92.                 GUI.contentColor = c;
    93.             }
    94.             else
    95.                 EditorGUI.LabelField(r[2], (att.max ?? maxVal).ToString());
    96.  
    97.             // Rounding you lose a tiny bit of precision but I don't mind
    98.             // it is just to show 0.84 instead of ugly 0.840041..
    99.             // unless you're in very small values (<0.1) then it doesn't round
    100.             minVal = FRound(minVal);
    101.             maxVal = FRound(maxVal);
    102.  
    103.             // Proofcheck
    104.             maxVal = Mathf.Max(att.min, maxVal);
    105.             minVal = Mathf.Clamp(minVal, att.min, maxVal);
    106.  
    107.             // And finally update the variables
    108.             if (EditorGUI.EndChangeCheck())
    109.             {
    110.                 minProperty.floatValue = minVal;
    111.                 property.floatValue = maxVal;
    112.             }
    113.         }
    114.         else
    115.             EditorGUI.HelpBox(ctrlRect, "MinTo is for Vector2 or float!!", MessageType.Error);
    116.     }
    117.  
    118.     const float threshold = .1f;
    119.     const float precision = .01f;
    120.     float FRound(float f) => f > threshold ? Mathf.Floor(f / precision) * precision : f;
    121.  
    122.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    123.     {
    124.         int lines = 1;
    125.         if (property.propertyType == SerializedPropertyType.Float)
    126.             lines = 2;
    127.         return lines * EditorGUIUtility.singleLineHeight;
    128.     }
    129.  
    130.     // That's orange
    131.     private Color overflowColor = new Color(1f, .55f, .1f, 1);
    132.  
    133.     public static Rect[] SplitRectIn3(Rect rect, int bordersSize, int space = 0)
    134.     {
    135.         var r = SplitRect(rect, 3);
    136.         int pad = (int)r[0].width - bordersSize;
    137.         int ps = pad + space;
    138.         r[0].width = r[2].width -= ps;
    139.         r[1].width += pad * 2;
    140.         r[1].x -= pad;
    141.         r[2].x += ps;
    142.         return r;
    143.     }
    144.     public static Rect[] SplitRect(Rect a, int n)
    145.     {
    146.         Rect[] r = new Rect[n];
    147.         for (int i = 0; i < n; ++i)
    148.             r[I] = new Rect(a.x + a.width / n * i, a.y, a.width / n, a.height);
    149.         return r;
    150.     }
    151. }
    152.  
     
    Last edited: Oct 18, 2018
  11. _watcher_

    _watcher_

    Joined:
    Nov 7, 2014
    Posts:
    261
    Thank you for this! I came to report a little bug with labels that i see in the new UI (2019.3.0f6). It might be easy fix, but i'm not that good at Editor Drawer scripting! (

    I used your above code, and this is the view i get:

    (labels scuffed)

    Hope this helps and thank you for your work!
     
    Last edited: Apr 6, 2020
  12. halo123proplem

    halo123proplem

    Joined:
    Nov 26, 2019
    Posts:
    2
    Awesome work, I needed to define a Random.Range min and max value in one variable and setting the MinTo property on a Vector2 give me exactly that, where x is the min and y is the max. Not exactly what I wanted but it works really well, thank you. @TommySKD
     
  13. ncr100

    ncr100

    Joined:
    Jul 23, 2015
    Posts:
    32
    +1 Thanks for sharing.

    My usage of @TommySKD variant (Attribute in Scripts/ folder, Drawer in Scripts/Editor folder, fixed the capital-I (eye) letter to be lowercase):

    Code (CSharp):
    1. public class SnakeWiggle : MonoBehaviour
    2. {
    3.     // ... cut
    4.     [MinTo(0.0f, 60.0f)]
    5.     [SerializeField] private Vector2 MoveEvery = new Vector2(0.25f, 15.0f);
    6.     // ... cut
    7. }
    upload_2023-7-24_10-23-46.png
     
    DucaDiMonteSberna likes this.
  14. Matheuz

    Matheuz

    Joined:
    Aug 27, 2014
    Posts:
    30
    I might be a bit late to the party, but I will add my contribution as well.

    I wrote a min/max range attribute that makes dealing with bounded ranges in the Unity inspector much easier and I thought it could help other people too. It supports both Vector2 and Vector2Int and custom floating-point decimal places (0 to 3). It's available on GitHub and Open UPM. To use it, just add the attribute to a field:
    Code (CSharp):
    1. [MinMaxRange(0f, 1f, 3)]
    2. [SerializeField] private Vector2 _normalTemperature = new (0.2f, 0.74f);
    And it will be displayed on the inspector using min/max sliders:

    Check the repository for more information on features, usage and how to add it to your project.
    Repository on GitHub: https://github.com/matheusamazonas/min_max_range_attribute
    Package on Open UPM: https://openupm.com/packages/com.sneakysquirrellabs.minmaxrangeattribute/
     
    Last edited: Jan 16, 2024
    Wulfburk and _watcher_ like this.