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

Bug Object Selector Window shows all Scriptable Object that inherit from Generic Scriptable Object

Discussion in 'Scripting' started by ThomasSoto, May 24, 2023.

  1. ThomasSoto

    ThomasSoto

    Joined:
    Aug 7, 2013
    Posts:
    26
    I'm trying to use Generics with Scriptable objects but the Object Selector Windows keeps showing all the classes that inherit from it.

    Code (CSharp):
    1.  
    2. // Generic SO
    3. public class ValueSO<T> : ScriptableObject {
    4.     [SerializeField] public T value;
    5. }
    6.  
    7. [CreateAssetMenu()]
    8. public class IntTestSO : ValueSO<int> {}
    9.  
    10. [CreateAssetMenu()]
    11. public class StringTestSO : ValueSO<string> {}
    12.  
    13. public class ScriptTest : MonoBehaviour {
    14.     public ValueSO<int> intValueTest;
    15. }
    As you can see on the last script, the field is supposed to be of type int, yet on the Object selection window it also shows SO of type string. This happens no matter the type as long as they inherit from ValueSO. Also notice that the type on the field appears as Value SO'1 instead of Value SO <int>

     
  2. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,900
    You meant to write:
    Code (CSharp):
    1. public class ScriptTest : MonoBehaviour {
    2.     public IntTestSO intValueTest;
    3. }
    instead?
    Last I checked Unity was only capable to handle concrete types, but it can accept generics as long as they are compatible according to the rules of generic types.
     
  3. ThomasSoto

    ThomasSoto

    Joined:
    Aug 7, 2013
    Posts:
    26
    No, I want to use it this way since this will go in a Wrapper, so it needs to use <T> for reuseability

    Code (CSharp):
    1. public class Wrapper <T> {
    2.     public T value;
    3.     public ValueSO<T> valueSO;
    4. }
     
  4. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,900
    Then you need to choose the proper types and filter them by hand. Unity will show all compatible typed objects for you.
    Unity's serialization is fast and dumb. It doesn't care about generic types, only concrete types. So you will need to make a concrete type to be able to filter down to specific generic type like:
    IntWrapper : Wrapper<int> {}
    and so on.
     
  5. ThomasSoto

    ThomasSoto

    Joined:
    Aug 7, 2013
    Posts:
    26
    Oh ok I see, I'll give that a try. Thank you!
     
    Lurking-Ninja likes this.
  6. ThomasSoto

    ThomasSoto

    Joined:
    Aug 7, 2013
    Posts:
    26
    Tried it and it still doesn't work . Or did I do it wrong?

    Code (CSharp):
    1. public class ScriptTest : MonoBehaviour {
    2.     public IntWrapper intValueTest;
    3. }
    4.  
    5. [System.Serializable]
    6. public class Wrapper <T> {
    7.     public T value;
    8.     public ValueSO<T> valueSO;
    9. }
    10.  
    11. [System.Serializable]
    12. public class IntWrapper : Wrapper<int> {
    13.  
    14. }
     
  7. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Unity can serialise it fine these days.

    As already mentioned in another thread, the object picker is dumb and has no concept of generics so you need custom inspectors to make this work.

    I'm not sure why OP deigned to make another thread about this.
     
    Lurking-Ninja likes this.
  8. ThomasSoto

    ThomasSoto

    Joined:
    Aug 7, 2013
    Posts:
    26
    Since the other post was overcomplicated with the way I explained it and wanted to double-check in case anyone else had another perspective on this. Sadly there is no way around it
     
  9. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    You can work around it like this:

    Code (CSharp):
    1. public abstract class ValueSO<T> : ScriptableObject
    2. {
    3.     [SerializeField]
    4.     public T value;
    5. }
    6.  
    7. [CreateAssetMenu()]
    8. public class IntTestSO : ValueSO<int> { }
    9.  
    10. [System.Serializable]
    11. public abstract class WrapperBase <T>
    12. {
    13.     public T value => valueSO ? valueSO.value : default(T);
    14.  
    15.     public abstract ValueSO<T> valueSO { get; }
    16. }
    17.  
    18. [System.Serializable]
    19. public class IntWrapper : WrapperBase<int>
    20. {
    21.     [SerializeField]
    22.     private IntTestSO _intSO;
    23.  
    24.     public override ValueSO<int> valueSO => _intSO;
    25. }
    26.  
    27. public class ScriptTest : MonoBehaviour
    28. {
    29.     public IntWrapper intValueTest;
    30. }
    Though I see this kind of pattern come up all the time and it always ends in an architectural nightmare.
     
    ThomasSoto likes this.