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. Join us on Dec 8, 2022, between 7 am & 7 pm EST, in the DOTS Dev Blitz Day 2022 - Q&A forum, Discord, and Unity3D Subreddit to learn more about DOTS directly from the Unity Developers.
    Dismiss Notice
  3. Have a look at our Games Focus blog post series which will show what Unity is doing for all game developers – now, next year, and in the future.
    Dismiss Notice

Serialized Generic with scriptable objects !

Discussion in '2020.1 Beta' started by DarylWinters, Jul 15, 2020.

  1. DarylWinters

    DarylWinters

    Joined:
    Jan 23, 2019
    Posts:
    5
    Hello,
    Since with Unity 2020.1 we're now able to serialize generic types, I was wondering if its possible to make the
    Code (CSharp):
    1. ScriptableObject.CreateInstance<GenericScriptableObject<...>()>
    works too in order to not to have to implement a non-generic class before.

    For exemple I have,
    Code (CSharp):
    1. public class Variable<T> : ScriptableObject
    2. {
    3.     private T _value = default;
    4.     public T Value
    5.     {
    6.         get => _value;
    7.         set => SetValue(value);
    8.     }
    9.  
    10.     public void SetValue(T value)
    11.     {
    12.         _value = value;
    13.     }
    14.  
    15. }
    and I want to be able to do
    Code (CSharp):
    1.  
    2.     public Variable<float> floatVariable;
    3.     void Start()
    4.     {
    5.         floatVariable = ScriptableObject.CreateInstance<Variable<float>>();
    6.     }
    (For now ScriptableObject.CreateInstance<Variable<float>>() just returns null)
    Thanks !
     
    marcospgp likes this.
  2. MrPaparoz

    MrPaparoz

    Joined:
    Apr 14, 2018
    Posts:
    156
    As far as I know you can achieve that via an static method on Variable class.
    Code (CSharp):
    1.  
    2. public class Variable<T> :  ScriptableObject {
    3.     public static Variable<T> Create<U>(UArgs args) where U : Variable<T>{
    4.          var instance = ScriptableObject.CreateInstance<U>();
    5.          //modify and init instance
    6.          return instance;
    7.     }
    8. }
     
  3. DarylWinters

    DarylWinters

    Joined:
    Jan 23, 2019
    Posts:
    5
    Sadly no ... it doesn't work
     
  4. print_helloworld

    print_helloworld

    Joined:
    Nov 14, 2016
    Posts:
    227
    Try in 20.2
     
  5. SolidAlloy

    SolidAlloy

    Joined:
    Oct 21, 2019
    Posts:
    58
    @Lelepoop Why did you suggest this? Is there anything in the changelog that made you think it is possible in 20.2?

    I investigated this a bit. The fact that CreateInstance() returns null is not the biggest obstacle. In the end, we can construct an empty scriptable object asset manually.
    When you create a Variable<T> script and select it in Unity, the inspector says "No MonoBehaviour scripts in the file, or their names do not match the file name". But we can't rename the file to "Variable<T>.cs", it is the OS limitation. Variable`1.cs doesn't help either. So, Unity must allow naming generic UnityEngine.Object classes to something like "Name`1.cs" on their C++ backend; only this way we will be able to create generic ScriptableObjects.
     
    marcospgp and PlayCreatively like this.
  6. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,314
    For testing purposes, can you add the component via editor scripting and check if it works? I'm wondering whether it's just an Inspector related issue.
     
  7. SolidAlloy

    SolidAlloy

    Joined:
    Oct 21, 2019
    Posts:
    58
    @Peter77 Since we are talking about ScriptableObjects here, I'm not sure I understood the "add the component" part right. Nevertheless, I created a generic MonoBehaviour like this:

    Code (CSharp):
    1. public class GenericMonoBehaviour<T> : MonoBehaviour
    2. {
    3.     [SerializeField] private T field;
    4. }
    And added it as a component via another script:
    var component = gameObject.AddComponent<GenericMonoBehaviour<string>>();


    The result is in the attached image. Also, AddComponent() returned null.
     

    Attached Files:

    Peter77 likes this.
  8. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,314
    My bad, I wasn't aware it's about ScriptableObject.

    Thanks for testing with a MonoBehavior, that's what I meant. Would be interesting to see if any magic method in that MonoBehavior, such as Awake/Start/Update etc, is called. If these are not called, I assume it's more than an Inspector issue and "allow naming generic" isn't the main culprit.
     
  9. SolidAlloy

    SolidAlloy

    Joined:
    Oct 21, 2019
    Posts:
    58
    @Peter77 I tried adding a non-generic MonoBehaviour with a wrong file name via a script, and it was added successfully. I didn't know it was possible, thanks for hinting at it!

    As for the generic MonoBehaviour, no event methods were called upon its creation (I also added the [ExecuteAlways] attribute just in case). In the end, it's not the naming problem. I think Unity just doesn't support UnityEngine.Object-derived generic classes yet. It would be cool to see this in the near future.
     
    Peter77 likes this.
  10. PlayCreatively

    PlayCreatively

    Joined:
    Jan 25, 2016
    Posts:
    94
    I'm in dire need of this feature and I can't understand why this isn't possible. I guess it has to do with unity objects being partly written in native code like @Bunny83 says here. I think @SolidAlloy is right.
    Is there a place where I can throw unity a suggestion directly? I've been struggling for weeks because of stuff like this not being properly ironed out. There's not even any acknowledgement from Unity in the documentations that this isn't possible.
     
    Last edited: Nov 4, 2020
    marcospgp likes this.
  11. SolidAlloy

    SolidAlloy

    Joined:
    Oct 21, 2019
    Posts:
    58
    This topic got me interested, and I created a proof-of-concept package that allows creating generic scriptable objects: Generic Unity Objects

    What you can do with it:
    • Implement your own generic ScriptableObjects (e.g. Generic<T>).
    • Create them from a context menu (similarly to the [CreateAssetMenu] attribute) and choose the generic argument type in the process.
    • Create object fields for generic ScriptableObjects and drag-and-drop the created assets to those fields.
    • Instantiate generic ScriptableObjects from scripts (but see the limitations.)
    How does it work?

    It generates a concrete ScriptableObject class for each asset with unique generic arguments you create. That is, when you create an asset of Generic<int>, it creates the Generic_Int : Generic<int> { } class. It then uses the concrete type to instantiate a generic scriptable object when you call GenericScriptableObject.CreateInstance<Generic<int>>()
    Your code should only know of Generic<int> but there will be a backing concrete class under the cover.

    I will appreciate any feedback. Hopefully, it will help someone.
     
    Last edited: Jan 30, 2021
  12. SolidAlloy

    SolidAlloy

    Joined:
    Oct 21, 2019
    Posts:
    58
    @PlayCreatively In this manual, they mention that custom classes cannot be serialized if they are generic (though they don't mention UnityEngine.Object-derived classes). They made custom generic classes serializable in Unity 2020, and I hoped ScriptableObjects can be generic too now, but nope. In the end, Unity has to find a comfortable way for users to choose generic argument types when they create a generic ScriptableObject asset or add a generic MonoBehaviour to an object. Making custom classes serializable is easier because it doesn't require UI changes.
     
    marcospgp and PlayCreatively like this.
  13. marcospgp

    marcospgp

    Joined:
    Jun 11, 2018
    Posts:
    151
    How about using generic custom classes and serializing them with [SerializeReference]?

    Should have the same effect as inheriting from ScriptableObject, no?

    Edit: Hmmm it seems not - SerializeReference prevents object duplication upon deserialization but only at the class level - so multiple references to the same object in a given MonoBehaviour would be preserved, but if another MonoBehaviour referenced that object I believe it would end up with a different copy.
     
    Last edited: Feb 4, 2022