Search Unity

Display A List<Class> With A Custom Editor Script

Discussion in 'Community Learning & Teaching' started by ForceX, Feb 12, 2014.

  1. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    This example will attempt to show you how to display any List<> array in the inspector when using a custom editor script with a few options. I pieced this together thorough various vaguely explained topics. I have tried to write this in general terms so it can be easily adapted to your needs.

    Lets say you have a List you want to be represented by a custom class inside of a custom inspector.


    Code (csharp):
    1. //Script name : CustomList.js
    2. import System; // Import the system class to give us access to List<>
    3.  
    4. //This is our custom class with our variables
    5. class MyClass{
    6. var AnGO : GameObject;
    7. var AnInt : int;
    8. var AnFloat : float;
    9. var AnVect3 : Vector3;
    10. }
    11.  
    12. //This is our list we want to use to represent our class as an array.
    13. var MyList : List.<MyClass> = new List.<MyClass>(0); // <-- We need to set an initial length for our list size or the editor will freak out
    14.  
    15. function AddNew(){
    16. //Add a new index position to the end of our list
    17. MyList.Add(new MyClass());
    18. }
    19.  
    20. function Remove(index : int){
    21. //Remove an index position from our list at a point in our list array
    22. MyList.RemoveAt(index);
    23. }
    24.  

    Because we hate the way the default inspector presents all our data lets create a custom editor for our inspector.

    O Nooo!! Unity does not support displaying LIST ARRRG!

    The work around....

    This example will show you how to display a list<> via two methods, size the list by defining a size with an int, add a new item to the list with a button, and remove any item from the list by it's index position with a button.

    Code (csharp):
    1.  
    2. @CustomEditor (CustomList)
    3.  
    4. class CustomListEditor extends Editor {
    5.     enum displayFieldType {DisplayAsAutomaticFields, DisplayAsCustomizableGUIFields}
    6.     var DisplayFieldType : displayFieldType;
    7.  
    8.     var GetTarget : SerializedObject;
    9.     var ThisList : SerializedProperty;
    10.     var ListSize : int;
    11.  
    12.     function OnEnable(){
    13.         GetTarget = new SerializedObject(target);
    14.         ThisList = GetTarget.FindProperty("MyList"); // Find the List in our script and create a refrence of it
    15.     }
    16.  
    17.     function OnInspectorGUI () {
    18.         //Update our list
    19.  
    20.         GetTarget.Update();
    21.    
    22.         //Choose how to display the list<> Example purposes only
    23.         EditorGUILayout.Space ();
    24.         EditorGUILayout.Space ();
    25.         DisplayFieldType = EditorGUILayout.EnumPopup("",DisplayFieldType);
    26.    
    27.         //Resize our list
    28.         EditorGUILayout.Space ();
    29.         EditorGUILayout.Space ();
    30.         EditorGUILayout.LabelField("Define the list size with a number");
    31.         ListSize = ThisList.arraySize;
    32.         ListSize = EditorGUILayout.IntField ("List Size", ListSize);
    33.  
    34.         if(ListSize != ThisList.arraySize){
    35.             while(ListSize > ThisList.arraySize){
    36.                 ThisList.InsertArrayElementAtIndex(ThisList.arraySize);
    37.             }
    38.             while(ListSize < ThisList.arraySize){
    39.                 ThisList.DeleteArrayElementAtIndex(ThisList.arraySize - 1);
    40.             }
    41.         }
    42.  
    43.     EditorGUILayout.Space ();
    44.     EditorGUILayout.Space ();
    45.     EditorGUILayout.LabelField("Or");
    46.     EditorGUILayout.Space ();
    47.     EditorGUILayout.Space ();
    48.  
    49.     //Or add a new item to the List<> with a button
    50.     EditorGUILayout.LabelField("Add a new item with a button");
    51.  
    52.     if(GUILayout.Button("Add New")){
    53.         target.MyList.Add(new MyClass());
    54.     }
    55.  
    56.     EditorGUILayout.Space ();
    57.     EditorGUILayout.Space ();
    58.  
    59.     //Display our list to the inspector window
    60.  
    61.     for(var i : int = 0; i < ThisList.arraySize; i++){
    62.         var MyListRef : SerializedProperty = ThisList.GetArrayElementAtIndex(i);
    63.         var MyInt : SerializedProperty = MyListRef.FindPropertyRelative("AnInt");
    64.         var MyFloat : SerializedProperty = MyListRef.FindPropertyRelative("AnFloat");
    65.         var MyVect3 : SerializedProperty = MyListRef.FindPropertyRelative("AnVect3");
    66.         var MyGO : SerializedProperty = MyListRef.FindPropertyRelative("AnGO");
    67.    
    68.  
    69.         // Display the property fields in two ways.
    70.    
    71.         if(DisplayFieldType == 0){// Choose to display automatic or custom field types. This is only for example to help display automatic and custom fields.
    72.             //1. Automatic, No customization <-- Choose me I'm automatic and easy to setup
    73.             EditorGUILayout.LabelField("Automatic Field By Property Type");
    74.             EditorGUILayout.PropertyField(MyGO);
    75.             EditorGUILayout.PropertyField(MyInt);
    76.             EditorGUILayout.PropertyField(MyFloat);
    77.             EditorGUILayout.PropertyField(MyVect3);
    78.         }else{
    79.             //Or
    80.        
    81.             //2 : Full custom GUI Layout <-- Choose me I can be fully customized with GUI options.
    82.             EditorGUILayout.LabelField("Customizable Field With GUI");
    83.             MyGO.objectReferenceValue = EditorGUILayout.ObjectField("My Custom Go", MyGO.objectReferenceValue, GameObject, true);
    84.             MyInt.intValue = EditorGUILayout.IntField("My Custom Int",MyInt.intValue);
    85.             MyFloat.floatValue = EditorGUILayout.FloatField("My Custom Float",MyFloat.floatValue);
    86.             MyVect3.vector3Value = EditorGUILayout.Vector3Field("My Custom Vector 3",MyVect3.vector3Value);
    87.         }
    88.    
    89.         EditorGUILayout.Space ();
    90.    
    91.         //Remove this index from the List
    92.         EditorGUILayout.LabelField("Remove an index from the List<> with a button");
    93.         if(GUILayout.Button("Remove This Index (" + i.ToString() + ")")){
    94.             ThisList.DeleteArrayElementAtIndex(i);
    95.         }
    96.         EditorGUILayout.Space ();
    97.         EditorGUILayout.Space ();
    98.         EditorGUILayout.Space ();
    99.         EditorGUILayout.Space ();
    100.     }
    101.  
    102.     //Apply the changes to our list
    103.     GetTarget.ApplyModifiedProperties();
    104.     }
    105. }
    106.  



    Here is the C# version. This version also describes how to use arrays inside of Class List.



    Code (csharp):
    1.  
    2. //Script name : CustomList.cs
    3. using UnityEngine;
    4. using System;
    5. using System.Collections.Generic; // Import the System.Collections.Generic class to give us access to List<>
    6.  
    7. public class CustomList : MonoBehaviour {
    8.  
    9.     //This is our custom class with our variables
    10.     [System.Serializable]
    11.     public class MyClass{
    12.         public GameObject AnGO;
    13.         public int AnInt;
    14.         public float AnFloat;
    15.         public Vector3 AnVector3;
    16.         public int[] AnIntArray = new int[0];
    17.     }
    18.  
    19.     //This is our list we want to use to represent our class as an array.
    20.     public List<MyClass> MyList = new List<MyClass>(1);
    21.  
    22.  
    23.     void AddNew(){
    24.         //Add a new index position to the end of our list
    25.         MyList.Add(new MyClass());
    26.     }
    27.  
    28.     void Remove(int index){
    29.         //Remove an index position from our list at a point in our list array
    30.         MyList.RemoveAt(index);
    31.     }
    32. }
    33.  
    34.  

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using UnityEditor;
    6.  
    7. [CustomEditor(typeof(CustomList))]
    8.  
    9. public class CustomListEditor : Editor {
    10.  
    11.     enum displayFieldType {DisplayAsAutomaticFields, DisplayAsCustomizableGUIFields}
    12.     displayFieldType DisplayFieldType;
    13.  
    14.     CustomList t;
    15.     SerializedObject GetTarget;
    16.     SerializedProperty ThisList;
    17.     int ListSize;
    18.  
    19.     void OnEnable(){
    20.         t = (CustomList)target;
    21.         GetTarget = new SerializedObject(t);
    22.         ThisList = GetTarget.FindProperty("MyList"); // Find the List in our script and create a refrence of it
    23.     }
    24.  
    25.     public override void OnInspectorGUI(){
    26.         //Update our list
    27.    
    28.         GetTarget.Update();
    29.    
    30.         //Choose how to display the list<> Example purposes only
    31.         EditorGUILayout.Space ();
    32.         EditorGUILayout.Space ();
    33.         DisplayFieldType = (displayFieldType)EditorGUILayout.EnumPopup("",DisplayFieldType);
    34.    
    35.         //Resize our list
    36.         EditorGUILayout.Space ();
    37.         EditorGUILayout.Space ();
    38.         EditorGUILayout.LabelField("Define the list size with a number");
    39.         ListSize = ThisList.arraySize;
    40.         ListSize = EditorGUILayout.IntField ("List Size", ListSize);
    41.    
    42.         if(ListSize != ThisList.arraySize){
    43.             while(ListSize > ThisList.arraySize){
    44.                 ThisList.InsertArrayElementAtIndex(ThisList.arraySize);
    45.             }
    46.             while(ListSize < ThisList.arraySize){
    47.                 ThisList.DeleteArrayElementAtIndex(ThisList.arraySize - 1);
    48.             }
    49.         }
    50.    
    51.         EditorGUILayout.Space ();
    52.         EditorGUILayout.Space ();
    53.         EditorGUILayout.LabelField("Or");
    54.         EditorGUILayout.Space ();
    55.         EditorGUILayout.Space ();
    56.    
    57.         //Or add a new item to the List<> with a button
    58.         EditorGUILayout.LabelField("Add a new item with a button");
    59.    
    60.         if(GUILayout.Button("Add New")){
    61.             t.MyList.Add(new CustomList.MyClass());
    62.         }
    63.    
    64.         EditorGUILayout.Space ();
    65.         EditorGUILayout.Space ();
    66.    
    67.         //Display our list to the inspector window
    68.  
    69.         for(int i = 0; i < ThisList.arraySize; i++){
    70.             SerializedProperty MyListRef = ThisList.GetArrayElementAtIndex(i);
    71.             SerializedProperty MyInt = MyListRef.FindPropertyRelative("AnInt");
    72.             SerializedProperty MyFloat = MyListRef.FindPropertyRelative("AnFloat");
    73.             SerializedProperty MyVect3 = MyListRef.FindPropertyRelative("AnVector3");
    74.             SerializedProperty MyGO = MyListRef.FindPropertyRelative("AnGO");
    75.             SerializedProperty MyArray = MyListRef.FindPropertyRelative("AnIntArray");
    76.  
    77.        
    78.             // Display the property fields in two ways.
    79.        
    80.             if(DisplayFieldType == 0){// Choose to display automatic or custom field types. This is only for example to help display automatic and custom fields.
    81.                 //1. Automatic, No customization <-- Choose me I'm automatic and easy to setup
    82.                 EditorGUILayout.LabelField("Automatic Field By Property Type");
    83.                 EditorGUILayout.PropertyField(MyGO);
    84.                 EditorGUILayout.PropertyField(MyInt);
    85.                 EditorGUILayout.PropertyField(MyFloat);
    86.                 EditorGUILayout.PropertyField(MyVect3);
    87.  
    88.                 // Array fields with remove at index
    89.                 EditorGUILayout.Space ();
    90.                 EditorGUILayout.Space ();
    91.                 EditorGUILayout.LabelField("Array Fields");
    92.  
    93.                 if(GUILayout.Button("Add New Index",GUILayout.MaxWidth(130),GUILayout.MaxHeight(20))){
    94.                     MyArray.InsertArrayElementAtIndex(MyArray.arraySize);
    95.                     MyArray.GetArrayElementAtIndex(MyArray.arraySize -1).intValue = 0;
    96.                 }
    97.  
    98.                 for(int a = 0; a < MyArray.arraySize; a++){
    99.                     EditorGUILayout.PropertyField(MyArray.GetArrayElementAtIndex(a));
    100.                     if(GUILayout.Button("Remove  (" + a.ToString() + ")",GUILayout.MaxWidth(100),GUILayout.MaxHeight(15))){
    101.                         MyArray.DeleteArrayElementAtIndex(a);
    102.                     }
    103.                 }
    104.             }else{
    105.                 //Or
    106.            
    107.                 //2 : Full custom GUI Layout <-- Choose me I can be fully customized with GUI options.
    108.                 EditorGUILayout.LabelField("Customizable Field With GUI");
    109.                 MyGO.objectReferenceValue = EditorGUILayout.ObjectField("My Custom Go", MyGO.objectReferenceValue, typeof(GameObject), true);
    110.                 MyInt.intValue = EditorGUILayout.IntField("My Custom Int",MyInt.intValue);
    111.                 MyFloat.floatValue = EditorGUILayout.FloatField("My Custom Float",MyFloat.floatValue);
    112.                 MyVect3.vector3Value = EditorGUILayout.Vector3Field("My Custom Vector 3",MyVect3.vector3Value);
    113.  
    114.  
    115.                 // Array fields with remove at index
    116.                 EditorGUILayout.Space ();
    117.                 EditorGUILayout.Space ();
    118.                 EditorGUILayout.LabelField("Array Fields");
    119.  
    120.                 if(GUILayout.Button("Add New Index",GUILayout.MaxWidth(130),GUILayout.MaxHeight(20))){
    121.                     MyArray.InsertArrayElementAtIndex(MyArray.arraySize);
    122.                     MyArray.GetArrayElementAtIndex(MyArray.arraySize -1).intValue = 0;
    123.                 }
    124.  
    125.                 for(int a = 0; a < MyArray.arraySize; a++){
    126.                     EditorGUILayout.BeginHorizontal();
    127.                     EditorGUILayout.LabelField("My Custom Int (" + a.ToString() + ")",GUILayout.MaxWidth(120));
    128.                     MyArray.GetArrayElementAtIndex(a).intValue = EditorGUILayout.IntField("",MyArray.GetArrayElementAtIndex(a).intValue, GUILayout.MaxWidth(100));
    129.                     if(GUILayout.Button("-",GUILayout.MaxWidth(15),GUILayout.MaxHeight(15))){
    130.                         MyArray.DeleteArrayElementAtIndex(a);
    131.                     }
    132.                     EditorGUILayout.EndHorizontal();
    133.                 }
    134.             }
    135.        
    136.             EditorGUILayout.Space ();
    137.        
    138.             //Remove this index from the List
    139.             EditorGUILayout.LabelField("Remove an index from the List<> with a button");
    140.             if(GUILayout.Button("Remove This Index (" + i.ToString() + ")")){
    141.                 ThisList.DeleteArrayElementAtIndex(i);
    142.             }
    143.             EditorGUILayout.Space ();
    144.             EditorGUILayout.Space ();
    145.             EditorGUILayout.Space ();
    146.             EditorGUILayout.Space ();
    147.         }
    148.    
    149.         //Apply the changes to our list
    150.         GetTarget.ApplyModifiedProperties();
    151.     }
    152. }
    153.  
    154.  
     
    Last edited: Jun 25, 2014
  2. nockieboy

    nockieboy

    Joined:
    Jul 7, 2012
    Posts:
    48
    ForceX - you're a star. Thanks for posting this. It's exactly what I'm trying to do - show a custom class list in the editor but with sliders/other custom controls for certain elements of the class. Brilliant and thanks again! :-D

    Ooh by the way, shouldn't myFloat use a FloatField and not an IntField? Just a thought. Can't wait to finish work now and try this code out!
     
  3. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Yup you are correct, thanks for pointing that out :)

    As for controlling the list size with a slider you can simply replace the ListSize IntField with the slider variant. I'll add in an example when I get off from work.
     
    TonCoder likes this.
  4. nockieboy

    nockieboy

    Joined:
    Jul 7, 2012
    Posts:
    48
    Ah - got home and tested the code.. unfortunately I'm working in C# and my attempts at converting your Javascript example are throwing up errors, most notably in this part of the code:

    Code (csharp):
    1. SerializedProperty MyListRef = ThisList.GetArrayElementAtIndex (i);    
    2.                                 SerializedProperty MyTitle = MyListRef.FindPropertyRelative ("title");         
    3.                                 SerializedProperty MyGO = MyListRef.FindPropertyRelative ("GO");               
    4.                                 SerializedProperty MyType = MyListRef.FindPropertyRelative ("type");
    5.                                 SerializedProperty MyDelay = MyListRef.FindPropertyRelative ("delay");
    6.                                 SerializedProperty MyDuration = MyListRef.FindPropertyRelative ("duration");
    7.                                 SerializedProperty MyAlpha = MyListRef.FindPropertyRelative ("alpha");
    8.                    
    9.                                 EditorGUILayout.LabelField ("Customizable Field With GUI");
    10.                                 //MyTitle.stringValue = EditorGUILayout.LabelField ("Tile", MyTitle.stringValue);
    11.                                 MyGO.objectReferenceValue = EditorGUILayout.ObjectField ("GO", MyGO, GameObject, true);
    12.                                 MyType.objectReferenceValue = EditorGUILayout.ObjectField ("Type", MyType.objectReferenceValue, ActionType, true);
    13.                                 MyDelay.floatValue = EditorGUILayout.FloatField ("Delay", MyDelay.floatValue);
    14.                                 MyDuration.floatValue = EditorGUILayout.FloatField ("Duration", MyDuration.floatValue);
    15.                                 MyAlpha.floatValue = EditorGUILayout.FloatField ("Alpha", MyAlpha.floatValue);
    It doesn't like this:
    Code (csharp):
    1. MyGO.objectReferenceValue = EditorGUILayout.ObjectField ("GO", MyGO, GameObject, true);
    Keeps saying that the expression denotes a 'type', where a 'variable' , 'value' or 'method group' was expected. Also, it didn't like the MyTitle.stringvalue = EditorGUILayout.LabelField("TTitle", MyTitle.stringValue); line at all - saying something about a void value.

    Any ideas?
     
    Last edited: Feb 12, 2014
    samana1407 likes this.
  5. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    I'm still at work so i cant confirm but you can try this.

    The MyGO needs to be set with the objectReferenceValue on both the left and right side of your EditorGUILayout.

    Code (csharp):
    1.  
    2. //Change this
    3. MyGO.objectReferenceValue = EditorGUILayout.ObjectField ("GO", MyGO, GameObject, true);
    4.  
    5. //To this
    6. MyGO.objectReferenceValue = EditorGUILayout.ObjectField ("GO", MyGO.objectReferenceValue , GameObject, true);
    7.  

    As for your string try using a TextField instead of a LabelField.

    Code (csharp):
    1.  
    2. //Change this
    3. MyTitle.stringValue = EditorGUILayout.LabelField ("Tile", MyTitle.stringValue);
    4.  
    5. //To this
    6. MyTitle.stringValue = EditorGUILayout.TextField("Tile", MyTitle.stringValue);
    7.  
    8. //Or
    9. EditorGUILayout.PropertyField(MyTitle);
    10.  
     
    Last edited: Feb 12, 2014
  6. nockieboy

    nockieboy

    Joined:
    Jul 7, 2012
    Posts:
    48
    Thanks for looking at this - sorry to bother you at work! ;-)

    The change to TextField worked fine for MyTitle, but I've still got the same error on this line:

    Code (csharp):
    1. MyGO.objectReferenceValue = EditorGUILayout.ObjectField ("GO", MyGO.objectReferenceValue, GameObject, true);
    This is the error:

    Assets/Editor/ActionListEditor.cs(61,155): error CS0119: Expression denotes a `type', where a `variable', `value' or `method group' was expected

    And also:

    Assets/Editor/ActionListEditor.cs(61,109): error CS1502: The best overloaded method match for `UnityEditor.EditorGUILayout.ObjectField(string, UnityEngine.Object, System.Type, params UnityEngine.GUILayoutOption[])' has some invalid arguments

    And:

    Assets/Editor/ActionListEditor.cs(61,109): error CS1503: Argument `#3' cannot convert `object' expression to type `System.Type'
     
    Last edited: Feb 12, 2014
  7. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Because you are using C# i think you have to decalere your GameObject with typeof(GameObject)

    Code (csharp):
    1. MyGO.objectReferenceValue = EditorGUILayout.ObjectField ("GO", MyGO.objectReferenceValue, typeof(GameObject), true);
     
  8. nockieboy

    nockieboy

    Joined:
    Jul 7, 2012
    Posts:
    48
    Yep, that's got it - thanks. One last problem - my custom list has another enum within it (ActionType) which I'm trying to display as an EnumPopup like so :

    Code (csharp):
    1. MyType.objectReferenceValue = (ActionType)EditorGUILayout.EnumPopup ("Type", MyType.objectReferenceValue);
    (Tried it with and without (ActionType) before EditorGUILayout... and it still bugs out.

    Unity is throwing this error:

    type is not a supported pptr value.

    I think it's not happy about MyType being a SerializedProperty but referring to an ActionType...? I'm still learning the intricacies of C# variable declarations and types it seems...

    *** SORTED ***

    Problem sorted. Looked back at some previous code I did to display an Enum that wasn't in a List and adapted it to your code - works fine now. Thanks very much for your help ForceX!! :-D
     
    Last edited: Feb 12, 2014
  9. nbg_yalta

    nbg_yalta

    Joined:
    Oct 3, 2012
    Posts:
    375
    Hey, I'm trying to convert this to the C# too,

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7. [CustomEditor(typeof(CustomList))]
    8.  
    9. public class CustomListEditor : Editor {
    10.    enum displayFieldType {DisplayAsAutomaticFields, DisplayAsCustomizableGUIFields}
    11.  
    12.    displayFieldType DisplayFieldType;
    13.    SerializedObject GetTarget;
    14.    SerializedProperty ThisList;
    15.    int ListSize;
    16.  
    17.    void OnEnable(){
    18.      GetTarget = new SerializedObject(target);
    19.      ThisList = GetTarget.FindProperty("MyList"); // Find the List in our script and create a refrence of it
    20.    }
    21.  
    22.    public override void OnInspectorGUI () {
    23.      //Update our list
    24.    
    25.      GetTarget.Update();
    26.    
    27.      //Choose how to display the list<> Example purposes only
    28.      EditorGUILayout.Space ();
    29.      EditorGUILayout.Space ();
    30.      DisplayFieldType = (displayFieldType)EditorGUILayout.EnumPopup("",DisplayFieldType);
    31.    
    32.      //Resize our list
    33.      EditorGUILayout.Space ();
    34.      EditorGUILayout.Space ();
    35.      EditorGUILayout.LabelField("Define the list size with a number");
    36.      ListSize = ThisList.arraySize;
    37.      ListSize = EditorGUILayout.IntField ("List Size", ListSize);
    38.    
    39.      if(ListSize != ThisList.arraySize){
    40.        while(ListSize > ThisList.arraySize){
    41.          ThisList.InsertArrayElementAtIndex(ThisList.arraySize);
    42.        }
    43.        while(ListSize < ThisList.arraySize){
    44.          ThisList.DeleteArrayElementAtIndex(ThisList.arraySize - 1);
    45.        }
    46.      }
    47.    
    48.      EditorGUILayout.Space ();
    49.      EditorGUILayout.Space ();
    50.      EditorGUILayout.LabelField("Or");
    51.      EditorGUILayout.Space ();
    52.      EditorGUILayout.Space ();
    53.    
    54.      //Or add a new item to the List<> with a button
    55.      EditorGUILayout.LabelField("Add a new item with a button");
    56.    
    57.      if(GUILayout.Button("Add New")){
    58.         target.MyList.Add(new MyClass());
    59.      }
    60.    
    61.      EditorGUILayout.Space ();
    62.      EditorGUILayout.Space ();
    63.    
    64.      //Display our list to the inspector window
    65.    
    66.      for(int i = 0; i < ThisList.arraySize; i++){
    67.        SerializedProperty MyListRef  = ThisList.GetArrayElementAtIndex(i);
    68.        SerializedProperty MyInt = MyListRef.FindPropertyRelative("AnInt");
    69.        SerializedProperty MyFloat= MyListRef.FindPropertyRelative("AnFloat");
    70.        SerializedProperty MyVect3= MyListRef.FindPropertyRelative("AnVect3");
    71.        SerializedProperty MyGO = MyListRef.FindPropertyRelative("AnGO");
    72.      
    73.      
    74.        // Display the property fields in two ways.
    75.      
    76.        if(DisplayFieldType == 0){// Choose to display automatic or custom field types. This is only for example to help display automatic and custom fields.
    77.          //1. Automatic, No customization <-- Choose me I'm automatic and easy to setup
    78.          EditorGUILayout.LabelField("Automatic Field By Property Type");
    79.          EditorGUILayout.PropertyField(MyGO);
    80.          EditorGUILayout.PropertyField(MyInt);
    81.          EditorGUILayout.PropertyField(MyFloat);
    82.          EditorGUILayout.PropertyField(MyVect3);
    83.        }else{
    84.          //Or
    85.        
    86.          //2 : Full custom GUI Layout <-- Choose me I can be fully customized with GUI options.
    87.          EditorGUILayout.LabelField("Customizable Field With GUI");
    88.          MyGO.objectReferenceValue = EditorGUILayout.ObjectField("My Custom Go", MyGO.objectReferenceValue, typeof(GameObject), true);
    89.          MyInt.intValue = EditorGUILayout.IntField("My Custom Int",MyInt.intValue);
    90.          MyFloat.floatValue = EditorGUILayout.FloatField("My Custom Float",MyFloat.floatValue);
    91.          MyVect3.vector3Value = EditorGUILayout.Vector3Field("My Custom Vector 3",MyVect3.vector3Value);
    92.        }
    93.      
    94.        EditorGUILayout.Space ();
    95.      
    96.        //Remove this index from the List
    97.        EditorGUILayout.LabelField("Remove an index from the List<> with a button");
    98.        if(GUILayout.Button("Remove This Index (" + i.ToString() + ")")){
    99.          ThisList.DeleteArrayElementAtIndex(i);
    100.        }
    101.        EditorGUILayout.Space ();
    102.        EditorGUILayout.Space ();
    103.        EditorGUILayout.Space ();
    104.        EditorGUILayout.Space ();
    105.      }
    106.    
    107.      //Apply the changes to our list
    108.      GetTarget.ApplyModifiedProperties();
    109.    }
    110. }
    111.  
    But getting this error:
    Assets/Editor/CustomListEditor.cs(57,32): error CS1061: Type `UnityEngine.Object' does not contain a definition for `MyList' and no extension method `MyList' of type `UnityEngine.Object' could be found (are you missing a using directive or an assembly reference?)
    Here
    Code (csharp):
    1.  
    2.      if(GUILayout.Button("Add New")){
    3.         target.MyList.Add(new MyClass());
    4.      }
    5.  
    Can you guys help me with this?
     
  10. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Sorry fo the late reply. I will update this later on today after work with a C# version.
     
  11. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Updated first post with C# versions of the script. The C# also shows how to use arrays inside of your custom Class <>List
     
  12. nbg_yalta

    nbg_yalta

    Joined:
    Oct 3, 2012
    Posts:
    375
    Thank you
     
  13. djfunkey

    djfunkey

    Joined:
    Jul 16, 2012
    Posts:
    201
    This little line here:
    Code (csharp):
    1. var MyList : List.<MyClass> = new List.<MyClass>(0); // <-- We need to set an initial length for our list size or the editor will freak out
    This fixed 1 year of having an error, that was just there... Thankyou :D
     
  14. Flickayy

    Flickayy

    Joined:
    Jan 20, 2013
    Posts:
    40
    Hey sorry for the little necro. I was wondering if you could show an example of how to use a ScriptableObject in this way?
     
  15. frank45

    frank45

    Joined:
    Apr 23, 2012
    Posts:
    26
    Fantastic friend...Still helping a lot..
     
  16. ZenMicro

    ZenMicro

    Joined:
    Aug 9, 2015
    Posts:
    206
    Hi ForceFX,

    I know this post is now ageing, however it looks to be useful only problem is i am having an issue with one line, not sure if Unity changed something so this no longer works or what.

    it's line 20 of the C# code - Editor Script;

    t =(CustomList)target;

    Error: The name 'target' does not exist in the current context.

    Not sure what that's supposed to be, I do not see any reference in your code to define target.

    EDIT: after further comparisons i should state that i am using a EditorWindow instead of Editor as well as public void OnGUI() and not public override void OnInspectorGUI(), and I can't say I am too familiar with the differences.

    I'm wondering if Property drawers were a new thing when this thread was started? - still digging deep into all of this.
     
    Last edited: Oct 28, 2015
  17. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Hi Zen,

    The "target" is intended for an editor class script. Unfortunately I'm not to familiar with the EditorWindow class.

    Have you tried to add the "using UnityEditor" to the top of your script?
     
  18. ZenMicro

    ZenMicro

    Joined:
    Aug 9, 2015
    Posts:
    206
    Hi, Yeah i have that include... so i'm just trying now to wrap my head around the differences between an Editor & an EditorWindow (which appears to be the best way to go for what i want to do)

    Many of the samples and tuts i see relate to editing a script attached to each object while what i am trying to accomplish is a stand alone editor(window) that takes a Parent object, then it will go through all the children and copy the transform data and fill in my custom class with that, then you can view a list of these and as my class has a number of other fields you can set min and max parameters for each and Apply these choices to the original (the purpose being random ranging the output back to the original transform).. a one off change.

    Anyway i'll keep searching (after some sleep - i'm spent after trying bits and pieces of a bunch of different people's code :p at least i did learn Editor and Editor Window are quite different, i got the feeling you could have an EditorWindow that could hold a list of Editors but i might be hallucinating (it's probably just a Class but with PropertyDrawers they could be displayed in a list all formatted nicely)...

    I should prbably just try your code as is and learn from that :) thanks btw.
     
  19. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    That's interesting I'm sorry I don't have a quick answer (no unity access atm). I guess when I eventually start working on creating a node editor window I'll have to figure it out.
     
  20. general656

    general656

    Joined:
    Apr 19, 2015
    Posts:
    1
    Hey, I made a small change in your code to be more organized in the UnityEditor :)
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEditor;
    5.  
    6. [CustomEditor(typeof(CustomList))]
    7.  
    8. public class CustomListEditor : Editor
    9. {
    10.  
    11.     enum displayFieldType { DisplayAsAutomaticFields, DisplayAsCustomizableGUIFields }
    12.     displayFieldType DisplayFieldType;
    13.  
    14.     CustomList t;
    15.     SerializedObject GetTarget;
    16.     SerializedProperty ThisList;
    17.     int ListSize;
    18.     List<bool> toggled = new List<bool>(); // Folded or not
    19.     void OnEnable()
    20.     {
    21.         t = (CustomList)target;
    22.         GetTarget = new SerializedObject(t);
    23.         ThisList = GetTarget.FindProperty("MyList"); // Find the List in our script and create a refrence of it
    24.     }
    25.  
    26.     public override void OnInspectorGUI()
    27.     {
    28.         //Update our list
    29.  
    30.         GetTarget.Update();
    31.  
    32.         //Choose how to display the list<> Example purposes only
    33.         EditorGUILayout.Space();
    34.         EditorGUILayout.Space();
    35.         DisplayFieldType = (displayFieldType)EditorGUILayout.EnumPopup("", DisplayFieldType);
    36.  
    37.         //Resize our list
    38.         EditorGUILayout.Space();
    39.         EditorGUILayout.Space();
    40.         EditorGUILayout.LabelField("Define the list size with a number");
    41.         ListSize = ThisList.arraySize;
    42.         ListSize = EditorGUILayout.IntField("List Size", ListSize);
    43.  
    44.         if (ListSize != ThisList.arraySize)
    45.         {
    46.             while (ListSize > ThisList.arraySize)
    47.             {
    48.                 ThisList.InsertArrayElementAtIndex(ThisList.arraySize);
    49.             }
    50.             while (ListSize < ThisList.arraySize)
    51.             {
    52.                 ThisList.DeleteArrayElementAtIndex(ThisList.arraySize - 1);
    53.             }
    54.         }
    55.  
    56.         EditorGUILayout.Space();
    57.         EditorGUILayout.Space();
    58.         EditorGUILayout.LabelField("Or");
    59.         EditorGUILayout.Space();
    60.         EditorGUILayout.Space();
    61.  
    62.         //Or add a new item to the List<> with a button
    63.         EditorGUILayout.LabelField("Add a new item with a button");
    64.  
    65.         if (GUILayout.Button("Add New"))
    66.         {
    67.             t.MyList.Add(new CustomList.MyClass());
    68.         }
    69.  
    70.         EditorGUILayout.Space();
    71.         EditorGUILayout.Space();
    72.  
    73.         //Display our list to the inspector window
    74.  
    75.         for (int i = 0; i < ThisList.arraySize; i++)
    76.         {
    77.             toggled.Add(false); // Initialliazed as false
    78.             SerializedProperty MyListRef = ThisList.GetArrayElementAtIndex(i);
    79.             SerializedProperty MyInt = MyListRef.FindPropertyRelative("AnInt");
    80.             SerializedProperty MyFloat = MyListRef.FindPropertyRelative("AnFloat");
    81.             SerializedProperty MyVect3 = MyListRef.FindPropertyRelative("AnVector3");
    82.             SerializedProperty MyGO = MyListRef.FindPropertyRelative("AnGO");
    83.             SerializedProperty MyArray = MyListRef.FindPropertyRelative("AnIntArray");
    84.  
    85.             toggled[i] = EditorGUILayout.Foldout(toggled[i], "[" + i +"]"); // Index Foldout
    86.             // Display the property fields in two ways.
    87.             if (toggled[i] == true)
    88.             {
    89.                 if (DisplayFieldType == 0)
    90.                 {// Choose to display automatic or custom field types. This is only for example to help display automatic and custom fields.
    91.                  //1. Automatic, No customization <-- Choose me I'm automatic and easy to setup
    92.                     EditorGUILayout.LabelField("Automatic Field By Property Type");
    93.                     EditorGUILayout.PropertyField(MyGO);
    94.                     EditorGUILayout.PropertyField(MyInt);
    95.                     EditorGUILayout.PropertyField(MyFloat);
    96.                     EditorGUILayout.PropertyField(MyVect3);
    97.  
    98.                     // Array fields with remove at index
    99.                     EditorGUILayout.Space();
    100.                     EditorGUILayout.Space();
    101.                     EditorGUILayout.LabelField("Array Fields");
    102.  
    103.                     if (GUILayout.Button("Add New Index", GUILayout.MaxWidth(130), GUILayout.MaxHeight(20)))
    104.                     {
    105.                         MyArray.InsertArrayElementAtIndex(MyArray.arraySize);
    106.                         MyArray.GetArrayElementAtIndex(MyArray.arraySize - 1).intValue = 0;
    107.                     }
    108.  
    109.                     for (int a = 0; a < MyArray.arraySize; a++)
    110.                     {
    111.                         EditorGUILayout.PropertyField(MyArray.GetArrayElementAtIndex(a));
    112.                         if (GUILayout.Button("Remove  (" + a.ToString() + ")", GUILayout.MaxWidth(100), GUILayout.MaxHeight(15)))
    113.                         {
    114.                             MyArray.DeleteArrayElementAtIndex(a);
    115.                         }
    116.                     }
    117.                 }
    118.                 else
    119.                 {
    120.                     //Or
    121.  
    122.                     //2 : Full custom GUI Layout <-- Choose me I can be fully customized with GUI options.
    123.                     EditorGUILayout.LabelField("Customizable Field With GUI");
    124.                     MyGO.objectReferenceValue = EditorGUILayout.ObjectField("My Custom Go", MyGO.objectReferenceValue, typeof(GameObject), true);
    125.                     MyInt.intValue = EditorGUILayout.IntField("My Custom Int", MyInt.intValue);
    126.                     MyFloat.floatValue = EditorGUILayout.FloatField("My Custom Float", MyFloat.floatValue);
    127.                     MyVect3.vector3Value = EditorGUILayout.Vector3Field("My Custom Vector 3", MyVect3.vector3Value);
    128.  
    129.  
    130.                     // Array fields with remove at index
    131.                     EditorGUILayout.Space();
    132.                     EditorGUILayout.Space();
    133.                     EditorGUILayout.LabelField("Array Fields");
    134.  
    135.                     if (GUILayout.Button("Add New Index", GUILayout.MaxWidth(130), GUILayout.MaxHeight(20)))
    136.                     {
    137.                         MyArray.InsertArrayElementAtIndex(MyArray.arraySize);
    138.                         MyArray.GetArrayElementAtIndex(MyArray.arraySize - 1).intValue = 0;
    139.                     }
    140.  
    141.                     for (int a = 0; a < MyArray.arraySize; a++)
    142.                     {
    143.                         EditorGUILayout.BeginHorizontal();
    144.                         EditorGUILayout.LabelField("My Custom Int (" + a.ToString() + ")", GUILayout.MaxWidth(120));
    145.                         MyArray.GetArrayElementAtIndex(a).intValue = EditorGUILayout.IntField("", MyArray.GetArrayElementAtIndex(a).intValue, GUILayout.MaxWidth(100));
    146.                         if (GUILayout.Button("-", GUILayout.MaxWidth(15), GUILayout.MaxHeight(15)))
    147.                         {
    148.                             MyArray.DeleteArrayElementAtIndex(a);
    149.                         }
    150.                         EditorGUILayout.EndHorizontal();
    151.                     }
    152.                 }
    153.  
    154.                 EditorGUILayout.Space();
    155.  
    156.                 //Remove this index from the List
    157.                 EditorGUILayout.LabelField("Remove an index from the List<> with a button");
    158.                 if (GUILayout.Button("Remove This Index (" + i.ToString() + ")"))
    159.                 {
    160.                     ThisList.DeleteArrayElementAtIndex(i);
    161.                 }
    162.                 EditorGUILayout.Space();
    163.                 EditorGUILayout.Space();
    164.                 EditorGUILayout.Space();
    165.                 EditorGUILayout.Space();
    166.             }
    167.         }
    168.  
    169.         //Apply the changes to our list
    170.         GetTarget.ApplyModifiedProperties();
    171.     }
    172. }
    173.  
    What I changed is that all indexes have a foldout choice, so you can go to the specific index you want to make directly the changes you want.
     
  21. imgodot

    imgodot

    Joined:
    Nov 29, 2013
    Posts:
    196
    Love this and I'm planning on trying it out later.
    Any of you gurus think it would be possible to use reflection to make this work generically for any class?
    I know that's a whole 'nother level, but I presume it's at least possible.
    -- Paul
     
  22. umurcg

    umurcg

    Joined:
    Jun 26, 2015
    Posts:
    2
    Thanks. With this editor I made a basic film editor for myself easily.

    By the way it gives and IndexofRangeException while this error does not prevent to compile it.

    IndexOutOfRangeException: Array index is out of range.
    UnityEditor.Editor.get_target () (at C:/buildslave/unity/build/artifacts/generated/common/editor/EditorBindings.gen.cs:121)
    FilmEditor.OnEnable () (at Assets/Editor/FilmEditor.cs:20)

    Do you have any idea about this exception?
     
  23. Fritsl

    Fritsl

    Joined:
    Mar 10, 2013
    Posts:
    210
    Hi ZenMicro, blast from an old tread here -> I'm stuck exactly where you where in 2015 :p
    Any way to get the 'Target' - issue to work with EditorWindow?

    PS: @ForceX: Thanks for making & sharing!!
     
  24. CaseyLee

    CaseyLee

    Joined:
    Jan 17, 2013
    Posts:
    40
    I used this as a template of sorts to build a nice custom editor (C#). Works well



    For this, I was getting a similar error at some point. I ended up replacing 'MyArray.arraySize' to a variable I got from the source array instead of the serialized property version. Getting the script to work took several similar tweaks and work-arounds like that. Might post my version if i get around to making it generic enough to share..
     
  25. sarachan

    sarachan

    Joined:
    Jul 21, 2013
    Posts:
    35
    Very helpful, thank you!
     
  26. SolleXelloS

    SolleXelloS

    Joined:
    Aug 30, 2010
    Posts:
    17
  27. jandd661

    jandd661

    Joined:
    Jun 25, 2017
    Posts:
    23
    This is still very helpful over 7 years later! Thank you.
     
    Vasilkb likes this.
  28. efrenaguilar95

    efrenaguilar95

    Joined:
    Mar 25, 2017
    Posts:
    1
    Thank you very much for this! It's still very helpful up to this day!
     
    Colin_MacLeod and asreed1111 like this.
  29. dr4

    dr4

    Joined:
    Jan 14, 2015
    Posts:
    74
    thanks to everyone who contributed to this post, found it in google and it is pure gold, enough examples to get everything on track
     
  30. multimediamarkers

    multimediamarkers

    Joined:
    Feb 17, 2016
    Posts:
    44
    Really great ... searched allmost the whole internet for such a great peace of C# code!!! Thanks!
     
  31. omyl

    omyl

    Joined:
    Jul 6, 2018
    Posts:
    1
    This post is pure gold. Thank you.
     
  32. GXMark

    GXMark

    Joined:
    Oct 13, 2012
    Posts:
    456


    An easy way to show a list with a custom class is as follows:

    Code (CSharp):
    1.  public override void OnInspectorGUI()
    2.         {
    3.             serializedObject.Update();
    4.  
    5.             GUI.color = Color.green;
    6.  
    7.             GUILayout.Label("ORC - Mouse Click Trigger", EditorStyles.boldLabel);
    8.  
    9.             GUI.color = Color.white;
    10.  
    11.             EditorGUILayout.Space();
    12.  
    13.             EditorGUILayout.LabelField("Property Block", EditorStyles.boldLabel);
    14.  
    15.             EditorGUILayout.BeginHorizontal();
    16.             EditorGUILayout.LabelField(new GUIContent("Button Mode"), GUILayout.MaxWidth(100));
    17.             EditorGUILayout.PropertyField(buttonMode, GUIContent.none);
    18.             EditorGUILayout.EndHorizontal();
    19.  
    20.             for (int i=0; i<properties.arraySize; i++)
    21.             {
    22.                 EditorGUILayout.BeginHorizontal();
    23.                 EditorGUILayout.LabelField(new GUIContent(properties.GetArrayElementAtIndex(i).FindPropertyRelative("name").stringValue), GUILayout.MaxWidth(100));
    24.                 EditorGUILayout.PropertyField(properties.GetArrayElementAtIndex(i).FindPropertyRelative("value"), GUIContent.none);
    25.                 EditorGUILayout.EndHorizontal();
    26.             }
    27.  
    28.             EditorGUILayout.Space();
    29.  
    30.             // Apply modfications to properties
    31.             serializedObject.ApplyModifiedProperties();
    32.        }
     
  33. Davon_Allen_AofL

    Davon_Allen_AofL

    Joined:
    Jul 17, 2019
    Posts:
    3
    i think an image is missing. not sure if its important.
     
    torresmthiago likes this.
  34. Mercbaker

    Mercbaker

    Joined:
    Apr 18, 2017
    Posts:
    18
    For a long time making custom editor stuff always confused me. But after reading through @ForceX 's method, it all just began to make sense!

    Wow, what a good post. Still useful in 2021!

    Thanks so much, I'm now writing my own editor variation and everything is just working perfectly.
     
unityunity