Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Create Custom Editor for custom class

Discussion in 'Scripting' started by SolitaryGaming, Dec 8, 2020.

  1. SolitaryGaming

    SolitaryGaming

    Joined:
    Nov 21, 2020
    Posts:
    10
    Edit: I've solved it, I was mistaking Property Drawers with Custom Editors.

    Hi

    I use a class called RangeInt that essentially works like a Vector2, except it has a few bells and a whistles that allow me to get a random number between it's two integers min and max, using either seeded random or random.range.

    I use it in a lot of my scripts to save me from retyping the randomization code constantly, but I have one complaint with it.

    RangeInt is a serialized class, and I use it in a lot of serializedfields. However instead of displaying like a Vector2 it has a dropdown menu with the min and max displaying vertically, making it take up a ton of room in my inspector.

    To fix this I've started trying to learn how to make custom editor windows, and this is what I came up with so far (I left out the unneccessary parts of RangeInt to save some space.)

    Code (CSharp):
    1. [System.Serializable]
    2. public class RangeInt
    3. {
    4.     public int min = 0;
    5.     public int max = 0;
    6.  
    7.     public RangeInt(int min, int max)
    8.     {
    9.         this.min = min;
    10.         this.max = max;
    11.     }
    12. }
    13.  
    14. [CustomEditor(typeof(RangeInt))]
    15. public class RangeIntEditor : Editor
    16. {
    17.     public override void OnInspectorGUI()
    18.     {
    19.         serializedObject.Update(); //Synchronizes RangeInt with component it represents.
    20.  
    21.         EditorGUILayout.BeginHorizontal();
    22.         RangeInt rangeInt = (RangeInt)target;
    23.         EditorGUILayout.LabelField("Min: "); EditorGUILayout.IntField(rangeInt.min);
    24.         EditorGUILayout.LabelField("Max: "); EditorGUILayout.IntField(rangeInt.max);
    25.         EditorGUILayout.EndHorizontal();
    26.  
    27.         serializedObject.ApplyModifiedProperties(); //Applies Changes
    28.     }
    29. }
    The issue I'm running into is RangeInt rangeInt = (RangeInt)target;
    It doesn't work because "Cannot convert type UnityEngine.Object to RangeInt."

    To fix this I tried to make RangeInt a subclass of Object, and it fixed that error, but made it so instead of either the old editor or the custom editor showing up where I want, I instead get a dropdown box prompting me to pick an object from my list (Which is what I reckoned would happen).

    What would be the correct solution to this? I'm sure there's something easy I'm misunderstanding, but this is my first time making a custom editor so I'm missing a lot of knowledge.

    I also wouldn't be surprised if there's something else not right with the script I've posted, but as I can't get it to display I can't test it yet to find out if the rest is fine or not.

    If it's relevant I use RangeInt inside of both scripts attatched to GameObjects, and inside of scriptable objects. If it's possible I would also like to be able to make custom editors for some of the scriptable objects I have and not have the RangeInt editor interfere with them, but I don't think that's something I have to be terribly concerned about.
     
    Last edited: Dec 9, 2020
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,762
    I tried your code as originally posted... saw the issue you reference. Could you perchance update the code above? It seems like a handy object to keep on hand!
     
  3. SolitaryGaming

    SolitaryGaming

    Joined:
    Nov 21, 2020
    Posts:
    10
    Yes it's been very handy. I find making my own little data structures like this has saved me a lot more time in dev than you'd expect.

    I wound up going with this:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. #if UNITY_EDITOR
    5. namespace CustomEditors
    6. {
    7.     [CustomPropertyDrawer(typeof(RangeInt))]
    8.     public class RangeIntEditor : PropertyDrawer
    9.     {
    10.         public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    11.         {
    12.             return EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2);
    13.         }
    14.         public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    15.         {
    16.             var _min = property.FindPropertyRelative("min");
    17.             var _max = property.FindPropertyRelative("max");
    18.             EditorGUI.BeginProperty(position, label, property);
    19.             {
    20.                 position.width = 100;
    21.                 EditorGUI.LabelField(position, label);
    22.  
    23.                 position.x += 125;
    24.                 position.width = 25;
    25.                 EditorGUI.LabelField(position, "Min: ");
    26.  
    27.                 position.x += 50;
    28.                 position.width = 35;
    29.                 EditorGUI.IntField(position, _min.intValue);
    30.  
    31.                 position.x += 50;
    32.                 position.width = 25;
    33.                 EditorGUI.LabelField(position, "Max: ");
    34.  
    35.                 position.x += 50;
    36.                 position.width = 35;
    37.                 EditorGUI.IntField(position, _max.intValue);
    38.  
    39.                 //EditorGUI.Vector2IntField(position, label, new Vector2Int(_min.intValue, _max.intValue));
    40.             }
    41.             EditorGUI.EndProperty();
    42.         }
    43.     }
    44. }
    45. #endif
    46.  
    The commented out Vector2Int field can be substituted for everything else in the horizontal group, I just chose to do it the other way because instead of "x" and "y" I wanted it to say Min and Max. If you're fine with x and y the Vector2IntField is arguably better in almost every way. It was just a stylistic choice.

    I should add in case you don't know:
    RangeInt and RangeIntEditor need to be in seperate files. RangeInt can be in whatever namespace and folder you want, but RangeIntEditor has to be in it's own file, in a folder called "Editor" (doesn't matter where the folder is, the name is all that matters,) and needs to be using whatever namespace RangeInt is in (Although RangeInt doesnt' need to use RangeIntEditor's namespace, hence why I keep my editor scripts in their own namespace.)

    If anybody knows how to make the position and width code better, please let me know. Typing in the values manually seems like a horrible way to do it and leads to a little bit of ugliness when I stretch out the inspector, but it does do the trick.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,762
    Hey SG, thanks for the update! I did notice one minor thing though: you don't assign the return from Editor.IntField() back to the appropriate field in the code above. I think the editable lines should read:

    Code (CSharp):
    1.                 _min.intValue = EditorGUI.IntField(position, _min.intValue);
    and

    Code (CSharp):
    1.                 _max.intValue = EditorGUI.IntField(position, _max.intValue);
    Otherwise the UI remains stubbornly immune to changes. :)
     
    Captainrad likes this.
  5. SolitaryGaming

    SolitaryGaming

    Joined:
    Nov 21, 2020
    Posts:
    10
    Ah you're correct I had it right in my project but when I removed some unnecessary code I had commented out I also somehow removed that bit .

    I wound up finding a free asset on the asset store called oneline that implements stuff like this but in attribute form. If you're struggling with the editor like I am you may find it worthwhile, not just to use but also because it's open source.
     
    ian_backlund likes this.
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,531
    Kurt-Dekker likes this.