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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

List of different object types with properties in inspector

Discussion in 'Scripting' started by RunningFlip, Jul 18, 2019.

  1. RunningFlip

    RunningFlip

    Joined:
    Jun 15, 2017
    Posts:
    4
    Hi!

    I'm currently searching for a solution to show a list of different objects from the same base class with there properties in the inspector, length of the list is also dynamic and may change.
    I do not find any solution for that, I found some good ideas with Editor-Scripting and "SerializedProperty", but this did not fit because "EntityComponent" does not implement "MonoBehaviour".
    Is there a solution for my problem?

    Code (CSharp):
    1. using System;
    2.  
    3. //Base class
    4. [Serializable]
    5. public class EntityComponent { }

    Code (CSharp):
    1. using System;
    2.  
    3. //An example for a class that inherits from EntityComponent
    4. [Serializable]
    5. public class TestComponent : EntityComponent
    6. {
    7.     public int number;
    8. }

    Code (CSharp):
    1.  
    2. //Holds a specific amount of different EntityComponents,
    3. //has to be drawn in the inspector with all there properties
    4. public class ComponentHolder
    5. {
    6.     private List<EntityComponent> components;
    7. }
     
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,835
    You can create a custom editor to put pretty much anything you want in the inspector window, but that would be a pretty complicated one. If you have a List<BaseClass> and populate it with instances of SubClass, I don't think Unity knows how to serialize it properly in order to restore it when you save and then load your scene, so you'd probably need to create separate List<T> variables for every possible subclass and then merge them together for display purposes.

    You'd need to update your custom editor (and probably also the associated MonoBehaviour) every time the list of possible subtypes changes.

    A better approach might be to have your BaseClass data type inherit from ScriptableObject, and then create all the data objects you want as scriptable objects, and drag them into your MonoBehaviour's inspector as references rather than "inline" data.
     
    RunningFlip likes this.
  3. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Indeed- displaying them wouldn't be particularly difficult, but serializing the extra data in sub-types would be extremely difficult. By default, only those fields common to the base type would be saved and loaded (in other words, you'll always save the items in the list as the base type, even if they're displayed as derived types- and even if the base type is abstract!).

    Using ScriptableObjects is likely the best approach if you store each item in the list individually as separate files. Every item in the list needs its own asset in order to maintain its identity properly, which means they all need to be ScriptableObjects, and then you can store them all in the same list of a base type because they're just being stored as references to those SO assets and not as values.

    Alternatively, you can look into OnBeforeSerialize and OnAfterDeserialize, and use your own serialization to maintain the data between saves and loads (this would be in the class that contains the list itself). One option for instance may be to use Json.NET and store the list contents as a string, converting the whole list to and from json every time a change is made. You'd still have to use a custom editor for displaying each item as its true (derived) type, which isn't quite trivial itself.

    Short answer is just that this isn't easy, and you're likely better off doing it some other way. For instance, is there a particular reason these need to be serialized in the inspector? If you're only building and using the list at runtime, you don't need to worry about any of this, only what serialization technique you'll use to save and load the list to a file if/when you need to. If you're really wanting to do it though, you can look into Odin Inspector, as I believe that can handle what you're trying to do, or at least make the process easier, but I've never actually tried to accomplish this so I can't say for certain.
     
    chemicalcrux and RunningFlip like this.
  4. RunningFlip

    RunningFlip

    Joined:
    Jun 15, 2017
    Posts:
    4
    Thank you both for the fast answers.
    The system I'm working on is an ECS, so I want to have my data components free from every functionallity,
    this is why my EntityComponents do not inherit from anything and do not implement methods.
    So using ScriptableObjects (what is usually a good point) does not fit in the strategy.

    I need to show my component datas in the inspector so I can check them easier and can also manipulate them.
    Actually I can deal with my problem pretty easily if I would find out how to dynamically draw objects with their properties in the inspector. It not have to be a list at all, it would be just fine with printing all the objects into the inspector itself, because i already know their types.
    I could iterate over the list, get the objects and cast them into their originally type, but is there a way to dynamically draw them into the inspector?

    It is totally fine for me if this will be pretty complex, because it is only used in the Editor itself and not in the final build.
     
  5. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Simply casting them won't work- you can get the fields to display, but it won't save the data that isn't shared with the type the collection is for. If the derived type has a string field for "SomeString", but the base type in List<BaseType> doesn't have that field, then even if you get the field to show up in the inspector, it won't actually save any data you type into it, and switching back and forth between one MonoBehaviour and another will simply wipe it.

    If that doesn't bother you, then watch a quick tutorial on PropertyDrawers, as that's likely what you'll be using. You could technically make a custom inspector for the MonoBehaviour type that contains your list, but you'll have to make a new one for every MonoBehaviour you want the list displayed in, and you'll have to manually draw not just the list and its contents, but every other serialized field you want to display in the inspector for the whole component. PropertyDrawers allow you to draw just serializable (non-UnityEngine.Object) types.

    Now, from what I recall, there's no way to draw generic types with a PropertyDrawer- T[] and List<T> are specially made exceptions built into the system, so if you're going to make a PropertyDrawer for a list, you'll actually have to extend it into a concrete type, like so:
    Code (csharp):
    1. public class SomeListType : List<BaseClass>
    2. {
    3.  
    4. }
    ... and then make the PropertyDrawer for that type instead, manually iterating over the collection and drawing each item in it. I don't know a way to shortcut drawing the individual derived object types though- you'll likely have to make some horrendous conditional tree or hardcoded lookup table so that you can draw each type differently and include their unique properties in the display. Some others I know a few years ago toyed around with using attributes and their custom setup similar to PropertyDrawers in order to simplify this task, but it's not something I followed closely as it seemed rather masochistic. Generally if you're going this far, it might be better to use Vexe's serialization system (VFW I think?) or Odin instead, since they did all of the work already (I know Vexe ended up regretting ever bothering though).

    Anyways, this isn't really in my wheel-house to give a clean set of instructions for how to manage it- my advice is simply not to do it. Sorry.

    I'd be remiss if I didn't mention though that ScriptableObjects are compatible inside and outside of Unity if you know how to read and edit YAML files. Our team on the other hand actually prefers using a web application (and SQL Server) over Unity inspectors for adding and editing pure data- we just download and dump it all into ScriptableObjects or JSON files at intervals, and avoid the Unity paradigm entirely. Lots of options there without going down this particular rabbit-hole.
     
    Last edited: Jul 19, 2019
    chemicalcrux likes this.