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

Display collection as field in editor window

Discussion in 'Scripting' started by mlepp, Aug 12, 2015.

  1. mlepp

    mlepp

    Joined:
    May 22, 2013
    Posts:
    26
    I'm working on an item database editor for my project. I've come a long way and can now create and edit my items, great! However I've designed my items so that items that are of the type weapon/armor (equipment items) have a serializable dictionary which will store the attributes for that item. For example, the dictionary of the item might contain a dictionary row with Key:Strength and Value:10.

    So now I want to integrate this field on my items into my editor window. What is the best way to do this? Should I design my own or is there a way to use the standard appearance of the serialized collection as it appears in the inspector?

    What I've tried:
    I tried using SerializedObject.FindProperty and then EditorGUILayout.PropertyField. Which got the field up in the editor window. However it added rows to the dictionary by itself everytime I saved the items. I might have messed up, but I'm not sure what I did wrong.

    The serializable dictionary:
    Code (csharp):
    1. public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
    2.     {
    3.         [SerializeField]
    4.         private List<TKey> _keys;
    5.         [SerializeField]
    6.         private List<TValue> _values;
    7.  
    8.         public SerializableDictionary()
    9.         {
    10.             _keys = new List<TKey>();
    11.             _values = new List<TValue>();
    12.         }
    13.  
    14.         public void OnBeforeSerialize()
    15.         {
    16.             foreach (var item in this)
    17.             {
    18.                 _keys.Add(item.Key);
    19.                 _values.Add(item.Value);
    20.             }
    21.         }
    22.  
    23.         public void OnAfterDeserialize()
    24.         {
    25.             for (int i = 0; i < _keys.Count; i++)
    26.             {
    27.                 Add(_keys[i], _values[i]);
    28.             }
    29.         }
    30.     }
     
  2. sz-Bit-Barons

    sz-Bit-Barons

    Joined:
    Nov 12, 2013
    Posts:
    150
    hmm.. I guess you have to write your own PropertyDrawer for this. it probably adds keys to it cause the underlying dictionary may not be empty when dezerializing. you should clear the collections at the right spot.
    here is an example: http://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.OnBeforeSerialize.html

    There are two other ways to get dictionary-like things working:
    1. make a variable with List<KeyValuePair<TKey, TValue>> and find the right entry with Linq expressions. (bad performance)
    2. Use another serialization system to get it working out of the box (like Full Inspector which is my favorite asset of all. I cannot think about using unity without it).
     
  3. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    I went a different direction with this entirely- I'll send you an assetbundle after I've made a few little modifications to get it more modular.
     
  4. mlepp

    mlepp

    Joined:
    May 22, 2013
    Posts:
    26
    Okey, I'll try to empty the dictionary. I should've thought of that...

    Sounds interesting :)
     
  5. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Here you go. A few comments about the package:
    • Everything is in the "Lysander.Items" namespace, so it won't interfere with any of your current scripts (I hope).
    • If you add the package, a new menu item will appear in Unity called "Databases", which has two options- "Item Attribute Manager" and "Item Manager". Attributes are essentially like "defense", "attack", "fire defense", etc... or at least that's how I intended them to be used. You can use them however you like, of course.
    • The first time you open either of the managers, it will create the databases that will be used. This is going to take a second, so don't freak out when it lags there. Subsequent times opening the manager will not lag, as it will just load the databases rather than creating them.
    • Attributes can be added to any item sub-type that's listed in the "can have attributes" static list in "ItemList". If you want your sub-type to support having attributes, list it there.
    • I mostly automated the process of adding a new item sub-type. The ones that I have there took like 10 seconds to add, each. To make a new one, just create a new file somewhere in the same namespace, with a class that inherits from Item, and then in ItemList script add a new entry to the static "type list" that corresponds with your new item type. That's it.
    • The Attribute Manager has an option for adding Default Attributes to a given item sub-type. Default attributes are attributes that automatically added to an item type when a new item of that type is instantiated. They're only added on item creation, and changing them doesn't affect items that already exist.
    • Made the option for attributes to either "have value" or "not have value" so that they can be used like flags, where appropriate.
    • Item attributes are listed (everywhere) by an internal priority number and not alphabetically. You can change this priority by using the up and down arrows in the manager to rearrange them. The "default attributes" list in there will update in real-time to reflect this priority, and the attribute lists on the items themselves will update to reflect the change the next time the item manager is opened. This is also the case for changes to the attribute name and whether or not it has a value.
    • I commented things rather excessively, so it's conversely a bit hard to navigate. I essentially use a nested class within the editor scripts that handle both holding the scriptableobject (database) list as well as little GUI bits and bobs that go with each item within that list (like whether a Foldout is open, where the scrollbar is, etc...). There's a big "database details" object for each database itself, and smaller "item details" objects for each item within that database details object.
    • The attribute manager and item manager have some dependencies on eachother, since items carry attributes (obviously). As such, the managers can't be open at the same time. You don't have to be concerned about it- when you open one it'll check if the other is open and, if it is, close it.
     

    Attached Files:

    Last edited: Aug 13, 2015
  6. mlepp

    mlepp

    Joined:
    May 22, 2013
    Posts:
    26
    @Lysander Sounds interesting, I'll have a look after work.