Search Unity

How To Make My Generic Collection Visible in Inspector

Discussion in 'Scripting' started by tahsinXYZ, Jan 23, 2021.

  1. tahsinXYZ

    tahsinXYZ

    Joined:
    Aug 14, 2019
    Posts:
    70
    Hello, I created my own collection using C#'s built in interfaces. How can I make my collection visible in every Incspector like list and array ? If you help I would be very apperciate!
     
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    The collection class needs to be marked as [Serializable] in order to show up. In addition, all the fields inside the class that you need to be saved needs to be marked with [SerializeField] (or be public - same rules as usual for showing up in the inspector)

    If you do that, it'll show up in the inspector, although it'll probably look a bit ugly. You can modify it by implementing a custom PropertyDrawer for the type.
     
    tahsinXYZ likes this.
  3. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    Without some custom (or third party -> odin inspector) serialization, generic types will not be shown in the inspector (and won't serialize).
    List<T> is the only generic type that unity supports out of the box.

    But you can do everything that @Baste says. You just need another step: create a class which derives from your generic type. That class must not be generic. It must define all type parameters of the base class. That class also needs to be marked as
    [Serializable] 
    . Fields of such types can be serialized and can be shown by the inspector.
     
    tahsinXYZ likes this.
  4. tahsinXYZ

    tahsinXYZ

    Joined:
    Aug 14, 2019
    Posts:
    70
    Did not work. Did you mean
    Code (CSharp):
    1. [System.Serializable]
    ?
     
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Yes
     
    tahsinXYZ likes this.
  6. tahsinXYZ

    tahsinXYZ

    Joined:
    Aug 14, 2019
    Posts:
    70
    It did not work again. Do
    ISerializationCallbackReceiver
    can solve my problem ?
     
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Did not work is very vague. Did nothing show up in the inspector? Did the data not serialize?

    How are you storing your data inside the collection? If it's internal storage is something Unity can't serialize, you'll need to use ISerailizationCallbackReceiver to solve that, yes.
     
    tahsinXYZ likes this.
  8. tahsinXYZ

    tahsinXYZ

    Joined:
    Aug 14, 2019
    Posts:
    70
    Here it's my collection:
    Code (CSharp):
    1.         [System.Serializable]
    2.         public class ExclusiveList<T>:ICollection<T>,IEnumerable<T>,IList<T> where T:class
    3.         {
    4.             [SerializeReference]
    5.             public List<T> innerList = new List<T>();
    6.  
    7.             public T this[int index]
    8.             {
    9.                 get => innerList[index];
    10.                 set
    11.                 {
    12.                     if (!innerList.Contains(value))innerList[index]=value;
    13.                 }
    14.             }
    15.  
    16.             public int Count => innerList.Count;
    17.             public bool IsReadOnly => false;
    18.  
    19.             public void Add(T item)
    20.             {
    21.                 if (!innerList.Contains(item)) innerList.Add(item);
    22.                 else throw new System.Exception("Item already exist.");
    23.             }
    24.             public void AddWithoutException(T item)
    25.             {
    26.                 if (!innerList.Contains(item)) innerList.Add(item);
    27.             }
    28.             public void Clear()
    29.             {
    30.                 innerList.Clear();
    31.             }
    32.             public bool Contains(T item)
    33.             {
    34.                 return innerList.Contains(item);
    35.             }
    36.             public void CopyTo(T[] array, int arrayIndex)
    37.             {
    38.                 innerList.CopyTo(array,arrayIndex);
    39.             }
    40.             public void CopyToList(ref List<T> list)
    41.             {
    42.                 foreach(T t in this)
    43.                 {
    44.                     list.Add(t);
    45.                 }
    46.             }
    47.             public void CopyToXList(ref ExclusiveList<T>xList)
    48.             {
    49.                 foreach (T t in this)
    50.                 {
    51.                     xList.AddWithoutException(t);
    52.                 }
    53.             }
    54.             public IEnumerator<T> GetEnumerator()
    55.             {
    56.                 return innerList.GetEnumerator();
    57.             }
    58.             public int IndexOf(T item)
    59.             {
    60.                 for(int i = 0; i < innerList.Count; i++)
    61.                 {
    62.                     if (innerList[i] == item)
    63.                     {
    64.                         return i;
    65.                     }
    66.                 }
    67.                 throw new System.Exception("No item found.");
    68.             }
    69.             public void Insert(int index, T item)
    70.             {
    71.                 if (index >= innerList.Count) throw new System.Exception("Index out of range.");
    72.                 else if (index < 0) throw new System.Exception("Index can't be negative.");
    73.                 else
    74.                 {
    75.                     if (!innerList.Contains(item)) innerList[index] = item;
    76.                     else throw new System.Exception("Item already exist.");
    77.                 }
    78.             }
    79.             public void InsertWithoutException(int index, T item)
    80.             {
    81.                 if (index >= 0 && index < Count && !innerList.Contains(item)) innerList[index]=item;
    82.             }
    83.             public bool Remove(T item)
    84.             {
    85.                 if (innerList.Contains(item))
    86.                 {
    87.                     innerList.Remove(item);
    88.                     return true;
    89.                 }
    90.                 else return false;
    91.             }
    92.             public void RemoveAt(int index)
    93.             {
    94.                 if (index >= innerList.Count) throw new System.Exception("Index out of range.");
    95.                 else if (index < 0) throw new System.Exception("Index can't be negative.");
    96.                 else innerList.RemoveAt(index);
    97.             }
    98.             public void RemoveAtWithoutException(int index)
    99.             {
    100.                 if (index < Count && index >= 0) innerList.RemoveAt(index);
    101.             }
    102.             public List<T> ReturnList()
    103.             {
    104.                 List<T> list = new List<T>();
    105.                 foreach(T t in this)
    106.                 {
    107.                     list.Add(t);
    108.                 }
    109.                 return list;
    110.             }
    111.             public System.Type GetListType()
    112.             {
    113.                 return typeof(T);
    114.             }
    115.             public int GetTypeAmount(System.Type type)
    116.             {
    117.                 int amount = 0;
    118.                 foreach(T t in innerList)
    119.                 {
    120.                     if (type == t.GetType()) amount++;
    121.                 }
    122.                 return amount;
    123.             }
    124.             IEnumerator IEnumerable.GetEnumerator()
    125.             {
    126.                 return innerList.GetEnumerator();
    127.             }
    128.         }
     
  9. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Copy-pasted your code into a Unity 2020.2.1f1 project, created this script;

    Code (csharp):
    1. public class TestScript : MonoBehaviour {
    2.     public ExclusiveList<Collider2D> colliderList;
    3. }
    It works as expected;

    upload_2021-1-25_16-33-34.png

    Note that you have to create a custom inspector in order for the exclusivity to be respected when adding objects, as the inspector just interacts directly with the inner list and none of your methods.
     
  10. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,990
    Note that in the past Unity's serialization system did not support any generic classes except the built-in List class. You always had to create a custom derived class that is not generic. I'm not sure from which Unity version on it supports generic types directly, however it has to be a quite recent one. I thought that it came with the "SerializeReference" support. However since this attribute is already used in this class here, I'm not really sure.
     
  11. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    It works for sure in 2020.2, it does for sure not work in 2019.4 (checked just now for unrelated reasons).
     
    Bunny83 and tahsinXYZ like this.
  12. tahsinXYZ

    tahsinXYZ

    Joined:
    Aug 14, 2019
    Posts:
    70
    Thanks for help I was using 2019.4!