Search Unity

[Released] Serializable Dictionary Lite - Now allowing custom editor for key field

Discussion in 'Assets and Asset Store' started by Rotary-Heart, Feb 19, 2018.

  1. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Great news, version 2.6.4 is now live!
     
    ibbybn likes this.
  2. qqqbbb

    qqqbbb

    Joined:
    Jan 13, 2016
    Posts:
    113
    Bug: If you have 2 inspector windows open the example database works properly with only 1 of them.
     
  3. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    That's an unfortunate known limitation since Unity seems to only detect the changes on a serialized field for the "main" inspector window, I don't know how it detects which one will be the "main". I've been trying to find a way to solve this, but so far I haven't found any way.
     
  4. genaray

    genaray

    Joined:
    Feb 8, 2017
    Posts:
    191
    @Rotary-Heart I really love your asset and im using it in my upcoming game for storing some references to the server database ! It works fine so far... with one little exception... im using a script reference system as some sort of plugin system... every serilizable dictionary entry should have exact one script attached... and thats where it gets worse.

    I can change GameObjects and other classes for each Entry... but when i try to change that ScriptReference it will change the Reference for all entrys of the dictionary... What can i do about this ?

    I could really need some help with this one...

    Example.PNG

    I marked the reference that keeps changing for all entrys of this dictionary with red... it only appears when i try to change the Entity Script of any entry... Changing other stuff works fine for each entry...

    //-------------------------------------------------------------------------------//

    // The ScriptReference ( From an imported asset called "RuntimeScriptField" )
    [System.Serializable]
    public class BaseEntityReference : ScriptReference<Base_Entity> { }
    // Your asset
    [Serializable]
    public class EntityDataHolder : SerializableDictionaryBase<string, EntityData> { }
    // DataHolder for the Dictionary
    [Serializable]
    public class EntityData
    {
    public int id;
    public string name;
    public GameObject visible;
    public GameObject transparent;
    // Once i try to change this one... instead of changing it for one entry... it changes for all dictionary entrys
    public BaseEntityReference entityScript;
    [Tooltip("This will be assigned by the connection manager, when he receives the certain options from the database.")]
    public List<OptionData> availableOptions;
    [Tooltip("The Entry that will displayed possible in the menu.")]
    public GameObject listingEntry;
    public ListingData listingData;
    }
    // The ScriptableObject
    [CreateAssetMenu(fileName = "EntityHolder", menuName = "ParallelOrigin/Map/Entitys/EntityHolder", order = 1)]
    public class EntityHolder : ScriptableObject {
    // The DataHolder as a part of the ScriptableObject for visualization
    [SerializeField]
    public EntityDataHolder entityDataHolder;
    }
     
    Last edited: Dec 18, 2018
  5. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    That's a super old version of the plugin, you should update first since that issue was fixed a while ago.
     
  6. genaray

    genaray

    Joined:
    Feb 8, 2017
    Posts:
    191
    @Rotary-Heart Thanks for your fast answer ! May i ask how this is possible ? I Updated your asset yesterday... and when i look at it, it just lets me reimport it... im using the unity 2017 lts version :)

    -- UPDATE --

    Sooo i just reimported the whole asset and i now see that it looks much better... so i guess it did the update :D

    Otherwise... the bug is still there... just tried to change the one reference and as i mentioned in my post above, it did change the reference for all entrys...

    Heres a video where i try to show the issue [Just open up the link for it... the embeded video quality is awfull]

     
    Last edited: Dec 19, 2018
  7. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Can you pm me your scripts needed to replicate this issue?
     
    genaray likes this.
  8. genaray

    genaray

    Joined:
    Feb 8, 2017
    Posts:
    191
    Just sended a PM ! :)
     
    Rotary-Heart likes this.
  9. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    This issue was "resolved". It wasn't an issue with this plugin, but with a custom property drawer.
     
  10. lolimilk

    lolimilk

    Joined:
    Dec 17, 2018
    Posts:
    15
    I have some problem
    I creat dictionary
    upload_2019-1-2_7-2-49.png

    I remove id "a" by editor button is ok,
    but in script use method remove it
    upload_2019-1-2_7-6-48.png
     
  11. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    What code are you using to remove it from that it's giving you that behaviour?
     
  12. lolimilk

    lolimilk

    Joined:
    Dec 17, 2018
    Posts:
    15
    [System.Serializable] public class DamageSetDict:SerializableDictionaryBase <string,DamageSet> {}

    public class DamageSystem: MonoBehaviour {

    public DamageSetDict damageSets;
    void OnStart(){
    damageSets.Remove(keyName);
    }
     
  13. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Alright I see the issue, the visual representation is not being updated. Here change the Remove function to this one
    Code (CSharp):
    1. public bool Remove(TKey key)
    2. {
    3.     if (_dict == null)
    4.         return false;
    5.  
    6.     _keyValues.Remove(key);
    7.     return _dict.Remove(key);
    8. }
    This will fix this issue.
     
  14. zIyaGtVm

    zIyaGtVm

    Joined:
    Dec 27, 2017
    Posts:
    131
    Hi, Does it support 2018.3 nested prefab workflow?
     
  15. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Hello,

    I don't see why it wouldn't work, but what are you trying to use it for exactly?
     
  16. zIyaGtVm

    zIyaGtVm

    Joined:
    Dec 27, 2017
    Posts:
    131
    Thanks for the quick reply! Actually I need store some different type GameObjects or Components in a Dictionary so that I can config these multiple type data on Inspector.
    I tried to serialize a dictionary like Dictionary<string, MyCustomClass> by Odin.
    Everything works fine until I pack them into nested prefab they told me there is some reason I can't modify the serialized Dictionary value in a [more than one layer prefab].o_O
    ie.png
    I wonder the risk depends on SerializedCustomDictionary within NestedPrefab , or it's just an Odin thing.
     
  17. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Alright so if I understand your use case correctly. You will have a dictionary on a nested prefab component that has data that you need to modify?
     
  18. zIyaGtVm

    zIyaGtVm

    Joined:
    Dec 27, 2017
    Posts:
    131
    Yes that's right.Is it a common risk for any way of serialization?
     
  19. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Well my best advice would be to give it a try. Here I did a quick and simple test and everything seems to be working fine.

    GIF.gif

    Edit: Sorry I forgot to turn on the setting to record my cursor, but hopefully you can see it working.
     
    zIyaGtVm likes this.
  20. zIyaGtVm

    zIyaGtVm

    Joined:
    Dec 27, 2017
    Posts:
    131
    Thanks a lot.I'll try it immediately!
     
  21. TenBitNet

    TenBitNet

    Joined:
    Nov 9, 2017
    Posts:
    29
    How do I make a key uneditable?
     
  22. TenBitNet

    TenBitNet

    Joined:
    Nov 9, 2017
    Posts:
    29
    I made my own script to prevent the keys from being removed and I don't have to worry about the edit part because it's a enum. Although anyone can modify it a little bit so that it will work the way they need it.

    This is the dictionary itself:

    Code (CSharp):
    1. using RotaryHeart.Lib.SerializableDictionary;
    2. using System;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. [System.Serializable]
    6. public class CharacterStats : SerializableDictionaryBase<CombatStats, int>, ISerializationCallbackReceiver
    7. {
    8.     private Dictionary<CombatStats, int> _tempDict= new Dictionary<CombatStats, int>();
    9.     protected override void DefaultBeforeSerialize()
    10.     {
    11.         base.DefaultBeforeSerialize();
    12.         _tempDict.Clear();
    13.         foreach (var item in m_dict)
    14.         {
    15.             _tempDict.Add(item.Key, item.Value);
    16.         }
    17.     }
    18.     protected override void DefaultAfterDeserialize()
    19.     {
    20.         base.DefaultAfterDeserialize();
    21.         foreach (CombatStats item in Enum.GetValues(typeof(CombatStats)))
    22.         {
    23.             if (!ContainsKey(item))
    24.             {
    25.                 Add(item, _tempDict[item]);
    26.             }
    27.         }
    28.     }
    29. }
    This is my edited version of SerializableDictionaryBase:

    Code (CSharp):
    1. //Based of the following thread https://forum.unity.com/threads/finally-a-serializable-dictionary-for-unity-extracted-from-system-collections-generic.335797/
    2.  
    3. using System.Collections.Generic;
    4.  
    5. namespace RotaryHeart.Lib.SerializableDictionary
    6. {
    7.     /// <summary>
    8.     /// This class is only used to be able to draw the custom property drawer
    9.     /// </summary>
    10.     public abstract class DrawableDictionary
    11.     {
    12.         [UnityEngine.HideInInspector]
    13.         public ReorderableList reorderableList = null;
    14.         [UnityEngine.HideInInspector]
    15.         public RequiredReferences reqReferences;
    16.         public bool isExpanded;
    17.     }
    18.  
    19.     /// <summary>
    20.     /// Base class that most be used for any dictionary that wants to be implemented
    21.     /// </summary>
    22.     /// <typeparam name="TKey">Key type</typeparam>
    23.     /// <typeparam name="TValue">Value type</typeparam>
    24.     [System.Serializable]
    25.     public class SerializableDictionaryBase<TKey, TValue> : DrawableDictionary, IDictionary<TKey, TValue>, UnityEngine.ISerializationCallbackReceiver
    26.     {
    27.         /// <summary>
    28.         /// I changed this to protected so I could access it. Also changed the name just for myself
    29.         /// </summary>
    30.         protected Dictionary<TKey, TValue> m_dict;
    31.         private readonly static Dictionary<TKey, TValue> _staticEmptyDict = new Dictionary<TKey, TValue>(0);
    32.  
    33.         /// <summary>
    34.         /// Copies the data from a dictionary. If an entry with the same key is found it replaces the value
    35.         /// </summary>
    36.         /// <param name="src">Dictionary to copy the data from</param>
    37.         public void CopyFrom(IDictionary<TKey, TValue> src)
    38.         {
    39.             foreach (var data in src)
    40.             {
    41.                 if (ContainsKey(data.Key))
    42.                 {
    43.                     this[data.Key] = data.Value;
    44.                 }
    45.                 else
    46.                 {
    47.                     Add(data.Key, data.Value);
    48.                 }
    49.             }
    50.         }
    51.  
    52.         /// <summary>
    53.         /// Copies the data from a dictionary. If an entry with the same key is found it replaces the value. Note that if the <paramref name="src"/> is not a dictionary of the same type it will not be copied
    54.         /// </summary>
    55.         /// <param name="src">Dictionary to copy the data from</param>
    56.         public void CopyFrom(object src)
    57.         {
    58.             var dictionary = src as Dictionary<TKey, TValue>;
    59.             if (dictionary != null)
    60.             {
    61.                 CopyFrom(dictionary);
    62.             }
    63.         }
    64.  
    65.         /// <summary>
    66.         /// Copies the data to a dictionary. If an entry with the same key is found it replaces the value
    67.         /// </summary>
    68.         /// <param name="dest">Dictionary to copy the data to</param>
    69.         public void CopyTo(IDictionary<TKey, TValue> dest)
    70.         {
    71.             foreach (var data in this)
    72.             {
    73.                 if (dest.ContainsKey(data.Key))
    74.                 {
    75.                     dest[data.Key] = data.Value;
    76.                 }
    77.                 else
    78.                 {
    79.                     dest.Add(data.Key, data.Value);
    80.                 }
    81.             }
    82.         }
    83.  
    84.         /// <summary>
    85.         /// Returns a copy of the dictionary.
    86.         /// </summary>
    87.         public Dictionary<TKey, TValue> Clone()
    88.         {
    89.             Dictionary<TKey, TValue> dest = new Dictionary<TKey, TValue>(Count);
    90.  
    91.             foreach (var data in this)
    92.             {
    93.                 dest.Add(data.Key, data.Value);
    94.             }
    95.  
    96.             return dest;
    97.         }
    98.  
    99.         /// <summary>
    100.         /// Returns true if the value exists; otherwise, false
    101.         /// </summary>
    102.         /// <param name="value">Value to check</param>
    103.         public bool ContainsValue(TValue value)
    104.         {
    105.             if (m_dict == null)
    106.                 return false;
    107.  
    108.             return m_dict.ContainsValue(value);
    109.         }
    110.  
    111.         #region IDictionary Interface
    112.  
    113.         #region Properties
    114.  
    115.         public TValue this[TKey key]
    116.         {
    117.             get
    118.             {
    119.                 if (m_dict == null) throw new KeyNotFoundException();
    120.                 return m_dict[key];
    121.             }
    122.             set
    123.             {
    124.                 if (m_dict == null) m_dict = new Dictionary<TKey, TValue>();
    125.                 m_dict[key] = value;
    126.             }
    127.         }
    128.  
    129.         public ICollection<TKey> Keys
    130.         {
    131.             get
    132.             {
    133.                 if (m_dict == null)
    134.                     m_dict = new Dictionary<TKey, TValue>();
    135.  
    136.                 return m_dict.Keys;
    137.             }
    138.         }
    139.  
    140.         public ICollection<TValue> Values
    141.         {
    142.             get
    143.             {
    144.                 if (m_dict == null)
    145.                     m_dict = new Dictionary<TKey, TValue>();
    146.  
    147.                 return m_dict.Values;
    148.             }
    149.         }
    150.  
    151.         public int Count
    152.         {
    153.             get
    154.             {
    155.                 return (m_dict != null) ? m_dict.Count : 0;
    156.             }
    157.         }
    158.  
    159.         bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
    160.         {
    161.             get { return false; }
    162.         }
    163.  
    164.         #endregion Properties
    165.  
    166.         public void Add(TKey key, TValue value)
    167.         {
    168.             if (m_dict == null)
    169.                 m_dict = new Dictionary<TKey, TValue>();
    170.             if (_keyValues == null)
    171.                 _keyValues = new List<TKey>();
    172.  
    173.             _keyValues.Add(key);
    174.  
    175.             m_dict.Add(key, value);
    176.         }
    177.  
    178.         public void Clear()
    179.         {
    180.             if (m_dict != null)
    181.                 m_dict.Clear();
    182.         }
    183.  
    184.         public bool ContainsKey(TKey key)
    185.         {
    186.             if (m_dict == null)
    187.                 return false;
    188.  
    189.             return m_dict.ContainsKey(key);
    190.         }
    191.  
    192.         public bool Remove(TKey key)
    193.         {
    194.             if (m_dict == null)
    195.                 return false;
    196.  
    197.             return m_dict.Remove(key);
    198.         }
    199.  
    200.         public bool TryGetValue(TKey key, out TValue value)
    201.         {
    202.             if (m_dict == null)
    203.             {
    204.                 value = default(TValue);
    205.                 return false;
    206.             }
    207.  
    208.             return m_dict.TryGetValue(key, out value);
    209.         }
    210.  
    211.         void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
    212.         {
    213.             if (m_dict == null) m_dict = new Dictionary<TKey, TValue>();
    214.             (m_dict as ICollection<KeyValuePair<TKey, TValue>>).Add(item);
    215.         }
    216.  
    217.         bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
    218.         {
    219.             if (m_dict == null) return false;
    220.             return (m_dict as ICollection<KeyValuePair<TKey, TValue>>).Contains(item);
    221.         }
    222.  
    223.         void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    224.         {
    225.             if (m_dict == null) return;
    226.             (m_dict as ICollection<KeyValuePair<TKey, TValue>>).CopyTo(array, arrayIndex);
    227.         }
    228.  
    229.         bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
    230.         {
    231.             if (m_dict == null) return false;
    232.             return (m_dict as ICollection<KeyValuePair<TKey, TValue>>).Remove(item);
    233.         }
    234.  
    235.         public Dictionary<TKey, TValue>.Enumerator GetEnumerator()
    236.         {
    237.             if (m_dict == null) return _staticEmptyDict.GetEnumerator();
    238.             return m_dict.GetEnumerator();
    239.         }
    240.  
    241.         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    242.         {
    243.             return GetEnumerator();
    244.         }
    245.  
    246.         IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
    247.         {
    248.             return GetEnumerator();
    249.         }
    250.  
    251.         #endregion
    252.  
    253.         #region ISerializationCallbackReceiver
    254.  
    255.         [UnityEngine.SerializeField]
    256.         private List<TKey> _keyValues;
    257.  
    258.         [UnityEngine.SerializeField]
    259.         private TKey[] _keys;
    260.         [UnityEngine.SerializeField]
    261.         private TValue[] _values;
    262.  
    263.         void UnityEngine.ISerializationCallbackReceiver.OnAfterDeserialize()
    264.         {
    265.             DefaultAfterDeserialize();
    266.         }
    267.  
    268.         /// <summary>
    269.         /// Turned this into a seperate a method so you can override it
    270.         /// </summary>
    271.  
    272.         protected virtual void DefaultAfterDeserialize()
    273.         {
    274.             if (_keys != null && _values != null)
    275.             {
    276.                 //Need to clear the dictionary
    277.                 if (m_dict == null)
    278.                     m_dict = new Dictionary<TKey, TValue>(_keys.Length);
    279.                 else
    280.                     m_dict.Clear();
    281.  
    282.                 for (int i = 0; i < _keys.Length; i++)
    283.                 {
    284.                     //This should only happen with reference type keys (Generic, Object, etc)
    285.                     if (_keys[i] == null)
    286.                     {
    287.                         //Special case for UnityEngine.Object classes
    288.                         if (typeof(UnityEngine.Object).IsAssignableFrom(typeof(TKey)))
    289.                         {
    290.                             //Key type
    291.                             string tKeyType = typeof(TKey).ToString();
    292.  
    293.                             //We need the reference to the reference holder class
    294.                             if (reqReferences == null)
    295.                             {
    296.                                 UnityEngine.Debug.LogError("A key of type: " + tKeyType + " requires to have a valid RequiredReferences reference");
    297.                                 continue;
    298.                             }
    299.  
    300.                             //Use reflection to check all the fields included on the class
    301.                             foreach (var field in typeof(RequiredReferences).GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic))
    302.                             {
    303.                                 //Only set the value if the type is the same
    304.                                 if (field.FieldType.ToString().Equals(tKeyType))
    305.                                 {
    306.                                     _keys[i] = (TKey)(field.GetValue(reqReferences));
    307.                                     break;
    308.                                 }
    309.                             }
    310.  
    311.                             //References class is missing the field, skip the element
    312.                             if (_keys[i] == null)
    313.                             {
    314.                                 UnityEngine.Debug.LogError("Couldn't find " + tKeyType + " reference.");
    315.                                 continue;
    316.                             }
    317.                         }
    318.                         else
    319.                         {
    320.                             //Create a instance for the key
    321.                             _keys[i] = System.Activator.CreateInstance<TKey>();
    322.                         }
    323.                     }
    324.  
    325.                     //Add the data to the dictionary. Value can be null so no special step is required
    326.                     if (i < _values.Length)
    327.                         m_dict[_keys[i]] = _values[i];
    328.                     else
    329.                         m_dict[_keys[i]] = default(TValue);
    330.                 }
    331.             }
    332.  
    333.             _keys = null;
    334.             _values = null;
    335.         }
    336.  
    337.         void UnityEngine.ISerializationCallbackReceiver.OnBeforeSerialize()
    338.         {
    339.             DefaultBeforeSerialize();
    340.         }
    341.  
    342.         /// <summary>
    343.         /// Turned this into a seperate a method so you can override it
    344.         /// </summary>
    345.  
    346.         protected virtual void DefaultBeforeSerialize()
    347.         {
    348.             if (m_dict == null || m_dict.Count == 0)
    349.             {
    350.                 //Dictionary is empty, erase data
    351.                 _keys = null;
    352.                 _values = null;
    353.             }
    354.             else
    355.             {
    356.                 //Initialize arrays
    357.                 int cnt = m_dict.Count;
    358.                 _keys = new TKey[cnt];
    359.                 _values = new TValue[cnt];
    360.  
    361.                 int i = 0;
    362.                 var e = m_dict.GetEnumerator();
    363.                 while (e.MoveNext())
    364.                 {
    365.                     //Set the respective data from the dictionary
    366.                     _keys[i] = e.Current.Key;
    367.                     _values[i] = e.Current.Value;
    368.                     i++;
    369.                 }
    370.             }
    371.         }
    372.  
    373.         #endregion
    374.  
    375.     }
    376. }
    377.  
     
  23. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Currently not implemented, with the current setup you can use a custom drawer for your key where you disable the GUI so it cannot be edited.
     
  24. TenBitNet

    TenBitNet

    Joined:
    Nov 9, 2017
    Posts:
    29
    I managed to do just by saving the value of the key temporarily then loading it back in after
     
  25. genaray

    genaray

    Joined:
    Feb 8, 2017
    Posts:
    191
    Well, in order to manage some ingame data i need to wrap the seri. dictionary into one of my own classes to add new methods and attributes to it... heres my unsuccessfull approach ( The scriptable object is just empty... nothing shows up )

    Code (CSharp):
    1. [Serializable]
    2. public class DataHolder<T> : SerializableDictionaryBase<string, T> {
    3.  
    4.     public virtual ListableData get(string key){
    5.  
    6.         return ((ListableData)((System.Object)this[key]));
    7.  
    8.     }
    9.  
    10. }
    11.  
    12.  
    13. [Serializable]
    14. public class ItemData : ListableData {
    15.  
    16.     public GameObject inventoryEntry;
    17.  
    18.     public GameObject item;
    19.  
    20.     public GameObject equipableItem;
    21.  
    22. }
    23.  
    24. [CreateAssetMenu(fileName = "ItemHolder", menuName = "ParallelOrigin/Items/ItemHolder", order = 1)]
    25. public class ItemHolder : ScriptableObject
    26. {
    27.  
    28.     [SerializeField]
    29.     public DataHolder<ItemData> itemDataHolder;
    30.  
    31. }
    Any idea why my approach doesnt work ?
     
  26. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    I'm not near my computer, but my first guess is that Unity doesn't serialize generics that way
    public DataHolder<ItemData>
    you need to wrap it on a class like this:

    Code (CSharp):
    1. [System.Serializable]
    2. public class MyClass : DataHolder<ItemData> {}
    3.  
    4. public MyClass itemDataHolder;
    If this doesn't fix it IllI check later today when Im back at my computer.
     
  27. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    @genaray Yes, the issue is what I told you. Unity doesn't serialize generics that way, even UnityEvents don't show if done that way. you need to wrap it on a class and it works:

    Code (CSharp):
    1. [Serializable]
    2. public class DataHolder<T> : SerializableDictionaryBase<string, T>
    3. {
    4.     public virtual ListableData get(string key)
    5.     {
    6.         return ((ListableData)((System.Object)this[key]));
    7.     }
    8. }
    9.  
    10. [Serializable]
    11. public class ItemData : ListableData
    12. {
    13.     public GameObject inventoryEntry;
    14.     public GameObject item;
    15.     public GameObject equipableItem;
    16. }
    17.  
    18. [CreateAssetMenu(fileName = "ItemHolder", menuName = "ParallelOrigin/Items/ItemHolder", order = 1)]
    19. public class ItemHolder : ScriptableObject
    20. {
    21.     [Serializable]
    22.     public class MyClass : DataHolder<ItemData> { }
    23.     public MyClass itemDataHolder;
    24. }
    Capture.PNG
     
    genaray likes this.
  28. genaray

    genaray

    Joined:
    Feb 8, 2017
    Posts:
    191
    Thanks a lot for your quick help !

    One more question... Is it possible to hide certain attributes from showing up ?

    Code (CSharp):
    1.  
    2.  
    3. [Serializable]
    4. public class ListableEntityData : InteractableData{
    5.  
    6.     public GameObject visible;
    7.     public GameObject transparent;
    8.  
    9.     public BaseEntityReference entityScript;
    10.  
    11.     .
    12.     .
    13.     .
    14.  
    15. }
    16.  
    17. [Serializable]
    18. public class ItemData : ListableEntityData {
    19.  
    20.     [HideInInspector]
    21.     public GameObject visible;  // ListableEntityData has the same attribute... trying to hide it somehow in itemdata.
    22.     [HideInInspector]
    23.     public GameObject transparent; //ListableEntityData has the same attribute... trying to hide it somehow in itemdata.
    24.     [HideInInspector]
    25.     public BaseEntityReference entityScript; // ListableEntityData has the same attribute... trying to hide it somehow in itemdata.
    26.  
    27.     public GameObject inventoryEntry;
    28.     public GameObject item;
    29.     public GameObject equipableItem;
    30.  
    31. }
    The class "ListableEntityData" already has the three attributes "visible", "transparent", "entityScript"... the class ItemData extends that class... but i dont want to show those three special attributes (Only in ItemData they shouldnt show up)... any tricks for that ? [HideInInspector] doesnt work...
     
    Last edited: Mar 4, 2019
  29. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Where do you have a ItemData Object? Is it a ScriptableObject?
     
    genaray likes this.
  30. genaray

    genaray

    Joined:
    Feb 8, 2017
    Posts:
    191
    Oh i am sorry.. .totall forgot to include that... it looks like that :

    Code (CSharp):
    1. [Serializable]
    2. public class ItemDataHolder : SerializableDictionaryBase<string, ItemData>{}
    3.  
    4. [CreateAssetMenu(fileName = "ItemHolder", menuName = "ParallelOrigin/Items/ItemHolder", order = 1)]
    5. public class ItemHolder : ScriptableObject{
    6.  
    7.     [SerializeField]
    8.     public ItemDataHolder itemDataHolder;
    9.  
    10. }
     
  31. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Ok wait I think I understand now what you are asking, you want to know how you could not show the parent class (ListableEntityData on this example) data inside your ItemData, but be able to display it on any other class?

    If that's the case your best bet is to have a PropertyDrawer and draw whatever attribute you want for that class
    Code (CSharp):
    1. [CustomPropertyDrawer(typeof(ItemData))]
    2. public class ItemDataDrawer : PropertyDrawer
    3. {
    4.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    5.     {
    6.         return EditorGUIUtility.singleLineHeight * 3;
    7.     }
    8.  
    9.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    10.     {
    11.         Rect pos = new Rect(position);
    12.         pos.height = EditorGUIUtility.singleLineHeight;
    13.  
    14.         EditorGUI.PropertyField(pos, property.FindPropertyRelative("inventoryEntry"));
    15.         pos.y += pos.height;
    16.         EditorGUI.PropertyField(pos, property.FindPropertyRelative("item"));
    17.         pos.y += pos.height;
    18.         EditorGUI.PropertyField(pos, property.FindPropertyRelative("equipableItem"));
    19.     }
    20. }
    Note that the drawer needs to be inside an Editor folder and it was just made to show how it would work. This outputs the following: As you can see it only shows the elements that I told it to draw, ignoring the ListableEntityData ones.
    Capture.PNG
     
    genaray likes this.
  32. genaray

    genaray

    Joined:
    Feb 8, 2017
    Posts:
    191

    Exactly ! Wonderfull and thanks a lot ! You are doing a really great job :D
     
    Rotary-Heart likes this.
  33. lolimilk

    lolimilk

    Joined:
    Dec 17, 2018
    Posts:
    15
    How Can I rename Key by Script?
     
  34. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    You want to rename an already existing key on the dictionary at runtime? If that is, you can't, you will need to remove the current element and add it back with the new key.
     
  35. lolimilk

    lolimilk

    Joined:
    Dec 17, 2018
    Posts:
    15
    i want to rename existing key on the dictionary in Editor not at runtime,but i want to use Script
     
  36. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    There is an update coming soon that will allow you to do this using the normal remove function. For now, no you cannot do that since the remove doesn't modify the arrays of keys and values, just the dictionary.
     
  37. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Version 2.6.5 has been submitted to the asset store for review. This version includes:
    • Bumped Unity version to 5.6.7
    • Included needed functionality to be able to modify the data by script either while playing or on editor.
    • Included bug fix mentioned on post #113
     
  38. LurchUSA

    LurchUSA

    Joined:
    Sep 20, 2017
    Posts:
    21
    Your dictionary is not removing the correct key.

    I have a serializable class as so:

    Code (CSharp):
    1.     [Serializable]
    2.     public class SelectedAttacher : SerializableDictionaryBase<int, int> { }
    The function I call to add/remove to the dictionary is this, a simple text box event toggle:

    Code (CSharp):
    1.         public void SelectToggleTextbox(Image selectOverlay, int mpId, int puId)
    2.         {
    3.  
    4.             if (!selectOverlay) return;
    5.  
    6.             GameObject soGo = selectOverlay.gameObject;
    7.  
    8.             if (soGo.activeSelf)
    9.             {
    10.                 soGo.SetActive(false);
    11.                 selectedAttacher.Remove(mpId);
    12.             }
    13.             else
    14.             {
    15.                 soGo.SetActive(true);
    16.                 selectedAttacher.Add(mpId, puId);
    17.             }
    18.         }
    The add function works well, however when I remove a given INT key, the last element is removed regardless of it's INT key value.

    Do I have to work with strings? Won't <int, int> work here?
     
  39. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Great news, version 2.6.5 is now live!

    @LurchUSA
    This is working perfectly fine on my end.
    Code (CSharp):
    1. [Serializable]
    2.     public class SelectedAttacher : SerializableDictionaryBase<int, int> { }
    3.  
    4.     [SerializeField] private SelectedAttacher selectedAttacher;
    5.  
    6.     // Update is called once per frame
    7.     void Update()
    8.     {
    9.         if (Input.GetKeyDown(KeyCode.A))
    10.             selectedAttacher.Add(15, 25);
    11.         if (Input.GetKeyDown(KeyCode.R))
    12.             selectedAttacher.Remove(15);
    13.     }
    GIF.gif
     
  40. lolimilk

    lolimilk

    Joined:
    Dec 17, 2018
    Posts:
    15
    modify the data by script on editor is very good !!
    but i have one problem,
    add key a
    add key b
    add key c
    → a b c
    remove key b
    → a c
    add key a
    → a a c why not is→ a c a
     
  41. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    I'm guessing that you used a again as explaining the issue, but not really adding another a since that wouldn't be added. So if you did find that you are able to add another a send me a repro code so I can fix this.

    To answer your question, because dictionaries do not guarantee order, they do not use order for storing the keys, they use a calculated hash. You can even see that on the gif I posted above. You can see the 15 trying to be added to the end (editor part) then the dictionary puts it in whatever order it calculates.
     
  42. lolimilk

    lolimilk

    Joined:
    Dec 17, 2018
    Posts:
    15
    oh ,I see that gif you posed.
    If i want 15 to be added to the end on editor, that is inpossible?
     
  43. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    With a dictionary it can't be forced, any reason you want it to be at the end?
     
  44. lolimilk

    lolimilk

    Joined:
    Dec 17, 2018
    Posts:
    15
    I have a problem,
    I modify the data by script on editor is ok.
    but restart Unity is not save change.
    After modifying by script.I click the directory onEditor and restart Unity is save successful.
     
  45. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Are you saving the file after you made the modifications? You will need to mark your Scriptable Object as dirty (or save it directly) after making any changes, otherwise Unity won't know that the file changed until you inspect it (click on it).
     
  46. lolimilk

    lolimilk

    Joined:
    Dec 17, 2018
    Posts:
    15
    I mean the dirctionary element not add, not value file no saved;
    example:
    use script dict.add(one element);
    → on editor have a on element (but I don't click it)
    → restart Unity
    → the dict no any any element
    (Sorry, I mean wrong, my English is not very good.)
     
  47. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    If you do what I told you above you should be able to save the values:
    Relevant links:
    https://docs.unity3d.com/ScriptReference/EditorUtility.SetDirty.html
    https://docs.unity3d.com/ScriptReference/AssetDatabase.SaveAssets.html

    Also, if you post your code I might be able to point you in the correct direction.
     
  48. lolimilk

    lolimilk

    Joined:
    Dec 17, 2018
    Posts:
    15
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using RotaryHeart.Lib.SerializableDictionary;
    4.  
    5. public class TestDict : MonoBehaviour
    6. {
    7.     public Dict dict;
    8. }
    9. [System.Serializable]
    10. public class Dict : SerializableDictionaryBase<string, int >{}
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. [CustomEditor(typeof(TestDict))]
    4. public class TestDictEditor : Editor
    5. {
    6.  
    7.     public override void OnInspectorGUI()
    8.     {
    9.         base.OnInspectorGUI();
    10.         TestDict testDict= (TestDict)target;
    11.         if (GUILayout.Button("Add")) {
    12.             testDict.dict.Add("a", 10);
    13.  
    14.         }
    15.     }
    16. }
    first on Inspecto
    upload_2019-3-26_11-44-25.png

    I click Add Butoon ( Than don't click dict on Editor and reStart Unity)
    upload_2019-3-26_11-46-15.png

    and then
    upload_2019-3-26_11-47-42.png
     
  49. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    The problem is that you are not saving the changes, you would need to save the changes you made (marking it dirty) so that they are not lost on restart. That's how the editor coding works. Here's how you can do it if the object is on a scene:
    Code (CSharp):
    1. if (GUILayout.Button("Add"))
    2. {
    3.     testDict.dict.Add("a", 10);
    4.     UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(testDict.gameObject.scene);
    5. }
    It will be different depending on your use case so you will need to search how to mark it as dirty or save the changes.
     
  50. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Version 2.6.6 has been submitted to the asset store for review. This version includes:
    • Patch to fix issue with advanced key dictionaries.