Search Unity

  1. We are migrating the Unity Forums to Unity Discussions. On July 12, the Unity Forums will become read-only.

    Please, do not make any changes to your username or email addresses at id.unity.com during this transition time.

    It's still possible to reply to existing private message conversations during the migration, but any new replies you post will be missing after the main migration is complete. We'll do our best to migrate these messages in a follow-up step.

    On July 15, Unity Discussions will become read-only until July 18, when the new design and the migrated forum contents will go live.


    Read our full announcement for more information and let us know if you have any questions.

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:
    157
    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:
    231
    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,650
    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,650
    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:
    205
    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
  14. Vaflov

    Vaflov

    Joined:
    Mar 4, 2019
    Posts:
    7