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.

Generic Scriptable Object Fields

Discussion in '2020.1 Beta' started by JakHussain, Dec 9, 2019.

  1. JakHussain

    JakHussain

    Joined:
    Oct 20, 2016
    Posts:
    318
    I was really excited about the new generic serialization update but when I tried it in my case I got an unexpected result.

    I have a generic class inheriting from a scriptable object and I also have subclasses that are serialized as concrete types so that I may create .asset files from them.

    What I wanted to do was to have a public field of MyGenericSO<float> or something like that and to then get an asset reference field in the inspector. Instead, the inspector showed the direct field of the class type which prevents me from dragging in my asset into the inspector.

    Is this by design or will this be resolved in the final release?
     
  2. JakHussain

    JakHussain

    Joined:
    Oct 20, 2016
    Posts:
    318
  3. tripodsgames

    tripodsgames

    Joined:
    Mar 19, 2016
    Posts:
    4
  4. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,578
    Could you show some example code, and a screenshot of what you see in the Inspector? I'm having trouble picturing what you're describing.
     
  5. JakHussain

    JakHussain

    Joined:
    Oct 20, 2016
    Posts:
    318
    Code (CSharp):
    1. public class SerlizationTest : MonoBehaviour
    2. {
    3.     public Wrapper<int> wrapper;
    4.     public ScriptableWrapper<int> shouldBeScriptableObject;
    5.     public ConcreteWrapper isSctriptableObject;
    6. }
    7.  
    8. [System.Serializable]
    9. public class Wrapper<T>
    10. {
    11.     public T value;
    12. }
    13.  
    14. [System.Serializable]
    15. public class ScriptableWrapper<T> : ScriptableObject
    16. {
    17.     public T value;
    18. }
    19.  
    20. public class ConcreteWrapper : ScriptableWrapper<int>
    21. {
    22.  
    23. }
    upload_2020-1-14_13-19-37.png

    The code above produces this result in the inspector which doesn't really make that much sense. The second field should render like the third, not the first so I would call this a bug.

    I also get an error in the console:

    UnityException: ScriptableObject.ctor is not allowed to be called during serialization, call it from Awake or Start instead. Called from MonoBehaviour 'SerlizationTest' on game object 'GameObject (1)'.
    See "Script Serialization" page in the Unity Manual for further details.
    UnityEngine.ScriptableObject..ctor () (at <0d01204b437e47c1a78b600b597a89f4>:0)
    ScriptableWrapper`1[T]..ctor () (at <247d1935aa774493a25c0b3378dd702d>:0)
     
    ModLunar likes this.
  6. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,578
    Agreed, this looks like a bug to me -
    shouldBeScriptableObject
    should indeed be rendered as a ScriptableObject field instead of expanded like that. Could you use the Bug Reporter to file that as a bug, and paste the number here?
     
    ModLunar likes this.
  7. JakHussain

    JakHussain

    Joined:
    Oct 20, 2016
    Posts:
    318
    The case number is: 1211768
     
  8. JakHussain

    JakHussain

    Joined:
    Oct 20, 2016
    Posts:
    318
    For those of you still coming across this post, the bug has been fixed as of 2020.1.0a22 which, at the time of writing, isn't publicly available yet but should be soon.
     
    TextusGames, ModLunar and SugoiDev like this.
  9. JakHussain

    JakHussain

    Joined:
    Oct 20, 2016
    Posts:
    318
    So I just tried Unity 2020.1.0a22 which DOES indeed fix the issue I described above but the functionality isn't quite complete by the looks of it:

    Code (CSharp):
    1. public class SerlizationTest : MonoBehaviour
    2. {
    3.     public Wrapper<int> wrapper;
    4.     public ScriptableWrapper<int> shouldBeScriptableObjectWithInt;
    5.     public ScriptableWrapper<string> shouldBeScriptableObjectWithString;
    6.     public IntWrapper isSctriptableObjectWithInt;
    7.     public StringWrapper isSctriptableObjectWithString;
    8. }
    9.  
    10. [System.Serializable]
    11. public class Wrapper<T>
    12. {
    13.     public T value;
    14. }
    15.  
    16. [System.Serializable]
    17. public class ScriptableWrapper<T> : ScriptableObject
    18. {
    19.     public T value;
    20. }
    21.  
    22. [CreateAssetMenu (menuName = "int wrapper")]
    23. public class IntWrapper : ScriptableWrapper<int>
    24. {
    25.  
    26. }
    27.  
    28. [CreateAssetMenu (menuName = "string wrapper")]
    29. public class StringWrapper : ScriptableWrapper<string>
    30. {
    31.  
    32. }
    33.  
    There's all my code and this is the result:

    upload_2020-2-7_13-8-2.png

    Ultimately, this does now produce fields in the inspector that only accept the correct types but I have 2 comments on this:
    1. In the "Should Be Scriptable Object" fields, the type specified on the right should say "Scriptable Wrapper<int>" or whatever the type is for that field rather than "Scriptable Wrapper`1" on both fields regardless of the type
    2. In the screenshots below I am dragging an instance of a concrete IntWrapper as a serialised asset over the string fields:
      upload_2020-2-7_13-13-42.png
      upload_2020-2-7_13-14-7.png
      The generic field's UI updates as if it would accept the input even though it DOESN'T accept the input which is actually the CORRECT outcome. In other words, functionally it works but the feedback in the UI is wrong. In the second image I'm dragging an IntWrapper into the StringWrapper field and this UI and outcome is correct. My mouse cursor even becomes a black no entry sign which doesn't happen in the first image.
    Is the team already aware of this? Should I submit another bug report / two bug reports?
     
    oscarAbraham, ModLunar and Peter77 like this.
  10. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,578
    Ah, thanks! Please submit a new bug report, yes. I think we'll probably prioritise this one a little lower than the previous one, as it's a UI glitch rather than a core data problem, but it should still get fixed.
     
    ModLunar and JakHussain like this.
  11. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    359
    @JakHussain seriously, thank you. This will be a huge improvement for my workflow knowing this is implemented/fixed, and thank you as well @superpig for responding and helping out too! :D Super excited to take advantage of the inspector + generics more now!
     
    JakHussain likes this.
  12. MrDizzle26

    MrDizzle26

    Joined:
    Feb 8, 2015
    Posts:
    32
    I'm using 2019.3.05f and I have a SO, which itself has a generic SO field with custom type passed in. This field doesn't even show in the inspector, is this part of the same problem?
     
  13. JakHussain

    JakHussain

    Joined:
    Oct 20, 2016
    Posts:
    318
    @MrDizzle26 This feature is only available in Unity 2020.1 which is still in alpha so if you really need to use this feature you'll need to upgrade. I'd suggest keeping a non upgraded version of your project aside in case the alpha causes some irreversible damage to your project (very unlikely).
     
  14. MrDizzle26

    MrDizzle26

    Joined:
    Feb 8, 2015
    Posts:
    32
    So I've downloaded Unity 2020.1, and my SO field shows in the inspector, but I am unable to assign a generic SO of a derived type to a field that expects a super. So polymorphism doesn't work for generic references?

    public abstract class Effect { }

    public class ChildEffect : Effect { }

    public abstract class StatEffect<T> : ScriptableObject where T : Effect
    {
    public T Effect;
    }

    // This is concrete SO
    public class ChildStatEffect : StatEffect<ChildEffect> { }

    public class SomeClass
    {
    // Unity won't let me assign ChildStatEffect into this slot in the inspector?
    StatEffect<Effect> StatEffect;​
    }
     
    Last edited: Feb 18, 2020
    ModLunar likes this.
  15. JakHussain

    JakHussain

    Joined:
    Oct 20, 2016
    Posts:
    318
    Try marking the objects that you pass as T to be serialisable? That would likely play a part in generically serialising it. In my example above I'm using primitives which are serialisable by default.
     
  16. MrDizzle26

    MrDizzle26

    Joined:
    Feb 8, 2015
    Posts:
    32
    What I wrote here is simplified sudo code in order to better explain my issue, in reality, all my classes are marked [Serializable]
     
  17. jasonatkaruna

    jasonatkaruna

    Joined:
    Feb 26, 2019
    Posts:
    64

    In C#, generics of different types are different classes (they are invariant). If you need to respect inheritance rules, then look into covariant/contravariant generics: https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance

    For example,

    Code (CSharp):
    1. public interface Invariant<T> { }
    2. public interface Covariant<out T> { }
    3. public interface Contravariant<in T> { }
    4.  
    5. Invariant<Base> genericBase = default;
    6. Invariant<Derived> genericDerived = default;
    7. genericBase = genericDerived; // err in compiler
    8. genericDerived = genericBase ; // err in compiler
    9.  
    10. Covariant<Base> genericBase = default;
    11. Covariant<Derived> genericDerived = default;
    12. genericBase = genericDerived ; // compiles
    13. genericDerived = genericBase ; // err in compiler
    14.  
    15. Contravariant<Base> genericBase = default;
    16. Contravariant<Derived> genericDerived = default;
    17. genericBase = genericDerived ; // err in compiler
    18. genericDerived = genericBase ; // compiles
     
    SolidAlloy, ModLunar and SugoiDev like this.
  18. SolidAlloy

    SolidAlloy

    Joined:
    Oct 21, 2019
    Posts:
    58
    @JakHussain Have you submitted a bug report regarding the representation of generic arguments in object picker? If so, could you share a link to IssueTracker? I see the bug isn't fixed yet in 2021, and I failed to find it on IssueTracker. I'd like to vote on it so that it is fixed sooner.
     
  19. Edvinas01

    Edvinas01

    Joined:
    Sep 15, 2015
    Posts:
    10
    I'm interested in voting on this bug report as well. Has it been submitted by any chance?

    Additionally I found that the object picker is not populated as well, even though the implementations exist (2020.3.11f1):
    upload_2021-6-7_16-31-58.png upload_2021-6-7_16-32-21.png
     
    RedGlow82 likes this.
  20. SolidAlloy

    SolidAlloy

    Joined:
    Oct 21, 2019
    Posts:
    58
    @Edvinas01 The issue was 'fixed' in 2021.2 https://issuetracker.unity3d.com/is...picker-field-when-scriptableobject-is-generic
    Now all assets inherited from BaseScriptableEvent<T> appear in the object selector window. However, if you create a BaseScriptableEvent<int> field, BaseScriptableEvent<string> or <bool> assets will also appear in the list, so it's not really a fix. I requested a further fix to which they replied it is more of a feature request than a bug report. They might consider adding it in the future but don't expect it to be fixed soon
     
    Edvinas01 likes this.
  21. ThomasSoto

    ThomasSoto

    Joined:
    Aug 7, 2013
    Posts:
    25
    I've been struggling for days trying to resolve this thinking it was an issue with my implementation. I can't believe they consider this a feature request when it's clearly a bug. In my case I would like to have over 20 inheritances from a Generic Scriptable Object, imagine how crazy it can get when all different SO with the wrong Type start showing up in the object picker.

    Unity please fix this! It would greatly improve a lot of workflows and give Generic Scriptable Object huge potential!
     
  22. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    359
    I recently tested on Unity 2023.2 Alpha and still doesn't seem like I can use ScriptableObject.CreateInstance<MyCustomType<int>>(), (for example) -- cause it'll just return null.

    Code (CSharp):
    1. public class MyCustomType<T> : ScriptableObject {
    2.    
    3. }
    It'd be really helpful to be able to create ScriptableObject assets that are with generic types passed in.
    It's a bit hectic/not scalable to have to define every type that can be passed in, since sometimes that may be a large number of types. I'd like to be able to rewire my game's architecture a bit by wrapping data in ScriptableObjects.

    (Tangentially related)