Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Custom Inspector - Lists inside Lists

Discussion in 'Scripting' started by Tiki, May 27, 2014.

  1. Tiki

    Tiki

    Joined:
    Mar 3, 2013
    Posts:
    299
    Hello,

    I'm having some trouble figuring out how to get Unity's Inspector to draw lists within lists. For instance, if you have a list of foldouts containing values, how do you make it possible to draw a list under each entry? I've tried using a jagged array ([][]), wrapped class type, and List<variable[]>, I keep getting null reference errors that when I try to fix, I reach what seems like limits in the built in "serializedObject" method or some other medley of errors that brings me back to where I started. At this point I'm not sure what code to give you to explain what I'm doing, but I'll try.

    In the actual script is:
    Code (csharp):
    1. public TypeWrapper[] BlockBuildBool;
    ...and at the end of it:
    Code (csharp):
    1. public class TypeWrapper : MonoBehaviour {
    2.  
    3.     public List<bool> buildTypes;
    4.  
    5.     public void initiate() {
    6.         buildTypes = new List<bool>();
    7.     }
    8.  
    9. }
    Then in the editor:
    Code (csharp):
    1. GUIContent blockLabel = new GUIContent("Block Types");
    2.         EditorList.Show (serializedObject.FindProperty("BlockNames"),serializedObject.FindProperty("BlockDensities"),serializedObject.FindProperty("BlockBool"),serializedObject.FindProperty("BlockBuildBool"),Script.BlockBuildBool,Script.BuildingNames.Length,blockLabel,EditorListOption.ParentList2);
    ... and the show function of editorlist class:
    Code (csharp):
    1. public static void Show (SerializedProperty list, SerializedProperty child, SerializedProperty child2, SerializedProperty child3, TypeWrapper[] child3Var, int limit, GUIContent label, EditorListOption options) {
    2.        
    3.         bool
    4.             asPList2 = (options  EditorListOption.ParentList2) != 0;
    5.  
    6.         while (child.arraySize > list.arraySize) {
    7.             child.arraySize--;
    8.         }
    9.        
    10.         while(child.arraySize < list.arraySize) {
    11.             child.arraySize++;
    12.         }
    13.  
    14.         while (child2.arraySize > list.arraySize) {
    15.             child2.arraySize--;
    16.         }
    17.        
    18.         while(child2.arraySize < list.arraySize) {
    19.             child2.arraySize++;
    20.         }
    21.  
    22.         while (child3.arraySize > list.arraySize) {
    23.             child3.arraySize--;
    24.         }
    25.        
    26.         while(child3.arraySize < list.arraySize) {
    27.             child3.arraySize++;
    28.         }
    29.  
    30.         if(asPList2){
    31.  
    32.             EditorGUILayout.PropertyField(list,label);
    33.             if(list.isExpanded){
    34.                 EditorGUI.indentLevel++;
    35.                 for(int i = 0;i < list.arraySize;i++){
    36.  
    37.                     GUIContent foldLabel = new GUIContent(list.GetArrayElementAtIndex(i).stringValue);
    38.                     EditorGUILayout.BeginHorizontal();
    39.                     child2.GetArrayElementAtIndex(i).boolValue = EditorGUILayout.Foldout(child2.GetArrayElementAtIndex(i).boolValue,foldLabel);
    40.                     DeleteButton(list,child,child2,i);
    41.                     EditorGUILayout.EndHorizontal();
    42.                     if(child2.GetArrayElementAtIndex(i).boolValue){
    43.  
    44.                         EditorGUILayout.PropertyField(list.GetArrayElementAtIndex(i),nameLabel);
    45.                         GUIContent densLabel = new GUIContent("Density:");
    46.                         EditorGUILayout.IntSlider(child.GetArrayElementAtIndex(i),0,4,densLabel);
    47.  
    48.  
    49.                     }
    50.                 }
    51.                 AddButton(list,child,child2);
    52.                 EditorGUI.indentLevel--;
    53.             }
    54.         }
    55.     }
    Assuming any items unrelated to child3, the intended nested list item, are properly declared and functioning, any suggestions on how to go about nesting a jagged array, wrapped type class or List<item[]>, so that each item in the parent list holds another list using the end level array?
     
  2. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Unity's Inspector cannot display any kind of nested collection (List<List<>>, type[][], type[,], List<type[]>, etc.) because it is unable to serialize it.

    What you have to do;

    Code (csharp):
    1.  
    2. public List<MyCollection> collection;
    3.  
    4. [Serializable]
    5. public class MyCollection
    6. {
    7.     public float[] innerCollection;
    8. }
    9.  
    If you are unable to make that work, it's most likely on your end, as Unity is able to display list of object properly.

    Also, you shouldn't need a custom editor for that.
     
  3. Tiki

    Tiki

    Joined:
    Mar 3, 2013
    Posts:
    299
    It's possible [Serializable] missing was the problem. I'm about to give that a try. I've run everything back to bare bones to make sure no false code was still involved, so it'll take me a moment to test that.

    There's a lot more going on in the code, so it's kind of necessary to make a custom inspector to prevent clutter.

    Thanks for the help.
     
  4. A.Killingbeck

    A.Killingbeck

    Joined:
    Feb 21, 2014
    Posts:
    483
    Like LightStriker said, Unity can't serialize nested collections, unless you separate them into Serialized classes.
     
  5. Tiki

    Tiki

    Joined:
    Mar 3, 2013
    Posts:
    299
    Ah, just took the right explanation I guess, I get it now. That was exactly the problem, thanks again guys!
     
  6. Tiki

    Tiki

    Joined:
    Mar 3, 2013
    Posts:
    299
    So this solved most of my problem it seemed, but now I'm having another error, and it seems I keep ending up back where I had started... Is there anywhere that a working nested lists example exists? I have to be missing something very small. I got the lists to all work, but for some reason, they only work after I've opened up the script and saved it, even though I'm not making changes. Until then it gives me a null reference error for the line where .Show is used.

    I'm sure this is the base of my problem, whatever it is I'm doing wrong. It's the strangest behaviour that I can attach my script to an object, get a null reference error, load the script and save it with no changes, then the script works fine. It's almost as if saving the script is somehow initializing the class array.
     
    Last edited: May 29, 2014
  7. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Code (csharp):
    1.  
    2. public List<bool> buildTypes = new List<bool>();
    3.  
    ?
     
  8. Tiki

    Tiki

    Joined:
    Mar 3, 2013
    Posts:
    299
    It seems to be the wrapper array that's giving off the null reference error, and saving the script with no changes, then going back to Unity seems to for some reason initialize it. I can't wrap my head around what I could have done to exhibit this behavior lol.
     
  9. Tiki

    Tiki

    Joined:
    Mar 3, 2013
    Posts:
    299
    Okay, so another mistake found. When trying to use the target method of accessing script variables alongside the serializedobject method of accessing script variables for editor purposes, you can get wonky results. With some thought, I replaced all my target instances with serialized paths and I'm no longer receiving this null reference error.

    The last problem I'm having, as all my results are displaying correctly and I can add to the lists with a button with no hitches, I cannot delete without it crashing my Unity to desktop. Unfortunately, this leaves me with no debug. I don't know if it's a botched loop, bad command, null reference, array index out of range... no idea. If anyone has ever had CTD's when trying to create a delete button, particularly one that would delete an array element containing a nested list such as this example, please let me know how you fixed it.
     
  10. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    That's rather vague. Can you remind me why you are writing a custom editor for an array?
     
  11. Tiki

    Tiki

    Joined:
    Mar 3, 2013
    Posts:
    299
    I know it's vague, that's the problem, it's crashing to desktop so I have no more information, I simply have been commenting things out to figure out what line is causing the problem.

    I'm not sure why you need to know the reason for making it a custom editor, but this is only one option in the script I'm writing. To leave it all displayed naturally would be a complete mess.

    At this point I've got the lists displaying properly, it's only an error with the deletion, which I'm sure is some kind of faulty loop I've written or a bad arraysize adjustment. The previous errors seem to be from my attempt to make direct targeting work alongside serialization methods. From what I can tell, both methods are executed at different times in the frame render, causing confliction issues when you try to adjust the array sizes unless you work in a complex system of checks that's unnecessary when you simply use serialization for everything. So I'd guess the deletion problem comes from the pointless check system I had been developing before getting a firmer grasp on the serialization commands. I'm not really asking for help at this point, just leaving a note for people who may have similar problems in the future, and also in unlikely hopes that someone sees this and just happens to have had the same problem and knows what to do.
     
  12. Tiki

    Tiki

    Joined:
    Mar 3, 2013
    Posts:
    299
    Well... that was quite a journey. I'm sure I can truncate something in my code, but it all works.

    The deletion problem was a two parter. As with the other things, it revolves around directly accessing a variable. I had to use the boolValue extension of serializedproperty to create a foldout, the result is more of the same confliction, even without targeting the script directly. Though I'm not sure why, I solved the CTD by having the boolean array for the foldout move the index in question to the end of the array, then shrinking the array by one with arraySize--, instead of using DeleteArrayElementAtIndex(). Then there was still an array index out of bounds error, which was simply because I had the delete button between the fill of the foldout variable and the recognition of the foldout variable, so it would fill the foldout with a value, delete it if you press the button, then try and find it again when it no longer exists. Putting the delete button within the recognition code or before it's even declared fixed this last bit. Hopefully someone in the future gets some use out of this thread. It sure put me through the ringer trying to figure it out without any example code to base off of.

    http://forum.unity3d.com/threads/247856-RELEASED-CityScaper-Procedural-City-Generator

    $newinterface.jpg
     
    Last edited: May 30, 2014
  13. DDeathlonger

    DDeathlonger

    Joined:
    May 9, 2014
    Posts:
    73
    lol need and want are separate yet equally accepted reasons for asking.. heh heh I'm trying to figure it out simply so I can change the "Element i" names but I have like 5 layer lists...