Search Unity

Custom Editor and derived classes of a list

Discussion in 'Scripting' started by Flynn_Prime, Aug 18, 2017.

  1. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    Let's say I have a List <Base> and this list has an element of different type (for example sake Sub). How can I make a custom inspector to display all the variables of the item added? I know unity doesn't serialize derived collection types (I'm getting around this with JSON.NET). I am able to serialize and deserialize my list correctly, but I would like to make all variables display in the Inspector if possible.

    Code (CSharp):
    1. [System.Serializable]
    2. public class Base
    3. {
    4. public int integer
    5.  
    6. public Base (int i)
    7. {
    8. this.integer = i;
    9. }
    10. }
    11.  
    12. [System.Serializable]
    13. public class Sub : Base
    14. {
    15. public string str;
    16.  
    17. public Sub (int i, string s) : base (i)
    18. {
    19. this.str = s;
    20. }
    21. }
     
  2. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @ItzChris92 - I can only offer my humble and hacky method, which I've tried at some point.

    The idea is you'd have standard classes Bar and SuperBar, where Bar has just int field value and SuperBar derived from Bar has float value...

    then I've added buttons to custom editor, which can be used to add Bars or SuperBars to list in BarTester, for which the Custom Inspector is for.

    This only works during runtime or in editor, press play or stop, and the subclass instances are gone naturally...

    And "all the variables" part of your question, in this case I've added type specific fields manually.

    Those who know better can correct if there is something terribly wrong.

    Code (csharp):
    1.  
    2. [CustomEditor(typeof(BarTester))]
    3. public class BarTesterEditor : Editor
    4. {
    5.  
    6.     public override void OnInspectorGUI()
    7.     {
    8.         BarTester t = (BarTester)target;
    9.  
    10.         // Hacky hack for demo
    11.         if (GUILayout.Button("Add Bar"))
    12.         {
    13.             t.AddBar();
    14.         }
    15.         if (GUILayout.Button("Add SuperBar"))
    16.         {
    17.             t.AddSuperBar();
    18.         }
    19.  
    20.         // Update list
    21.         if (t.list != null) UpdateList (t.list);
    22.     }
    23.  
    24.     void UpdateList(List<Bar> list)
    25.     {
    26.         foreach (var item in list)
    27.         {
    28.             if (item.GetType() == typeof(Bar))
    29.             {
    30.                 EditorGUILayout.LabelField("Bar:");
    31.                 item.value = EditorGUILayout.IntField(item.value);
    32.             }
    33.             if (item.GetType() == typeof(SuperBar))
    34.             {
    35.                 SuperBar s = item as SuperBar;
    36.  
    37.                 EditorGUILayout.LabelField("SuperBar:");
    38.                 s.value =  EditorGUILayout.IntField(item.value);
    39.                 s.superValue = EditorGUILayout.FloatField(s.superValue);
    40.             }
    41.         }
    42.     }
    43.  
    44. }
    45.  
     
    Kiwasi likes this.
  3. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    The inspector will only show the data for the object type specified (unless you make a custom inspector like @eses).

    If you want to configure the variables for a Sub object in the inspector, and still include it in the Base list, you could make Base a ScriptableObject. Then you can create new instances of Base or Sub as data assets in your project files, customize each one in the inspector, and then add them all to the Base list. The Sub data will be preserved even though the list is of type Base. You can then cast to type Sub to retrieve the Sub-specific data later.
     
  4. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    So even if I use a custom editor it won't be viewable at run time? That's what I require really.

    As for scriptable objects, I don't understand them enough to begin using them properly (it's my understanding that variables in a scriptable object can't be changed?). My items will include weapons and equipment which can level up, resulting in variable changes
     
  5. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    I'm not sure what you mean by viewable at runtime, do you mean playing in the editor?

    If you mean literally editable during runtime sessions in a build, that's not what they're for. If you're talking about saving data based on gameplay progression, and then loading it the next session, you can look in to PlayerPrefs, or creating your own json based save system using the JSONUtility class.

    As for scriptable objects, you can think of them as data containers that you configure in your project files, and then use as convenient, object-oriented modules of data and functions that other scripts can use. You can edit them during play-in-editor sessions and the changes will be saved. Once you build the game though, they won't respect changes at runtime as far as I know.

    Give this a watch, it's very insightful.
    https://unity3d.com/learn/tutorials/modules/beginner/live-training-archive/scriptable-objects
     
    Flynn_Prime likes this.
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    You will. It's not anything special, you just have to draw the list manually:

    Code (csharp):
    1. foreach(var baseOrSub in baseList) {
    2.     baseOrSub.integer = EditorGUILayout.IntField("Integer", baseOrSub.integer);
    3.  
    4.     var asSub = baseOrSub as sub;
    5.     if(asSub != null) {
    6.         asSub.str = EditorGUILayout.InputField("Str", asSub.str);
    7.     }
    8. }
    You probably want to format it a lot more nicely, but that'll do the trick.

    You'll want to mark the List itself with [HideInInspector], so Unity doesn't draw it as a bunch of Base references. Actually, since you're using custom serialization, you could just not mark Base or Sub as serializable, unless your json library requires that.
     
    Flynn_Prime likes this.