Scripting: The serializer can now serialize fields of generic types (e.g. MyClass someField) directly; it is no longer necessary to derive a concrete subclass from a generic type in order to serialize it. I'd argue this beauty needs its own entry in the 2020.1 features list!
That's neat! I'd love to know how this interacts with property drawers. How would we go about defining a custom property drawer for a generic class?
Code (CSharp): [CustomPropertyDrawer(typeof(Container<>))] public class ContainerPropertyDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.PropertyField(position, property.FindPropertyRelative("Value"), new GUIContent(fieldInfo.FieldType.GetGenericArguments()[0].FullName)); } } [CustomPropertyDrawer(typeof(Container<string>))] public class StringContainerPropertyDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.PropertyField(position, property.FindPropertyRelative("Value"), new GUIContent("String!")); } } This works. You can get the generic argument type with fieldInfo.FieldType.GetGenericArguments(). Plus it looks like it picks the more specific attribute if it exists.
Cool, thanks. That'll be really, really useful. We have a SerializableDictionary implementation, and each of them requires both a concrete subclass and a concrete drawer subclass, so this will save us a ton of code.
What about generic SerializeReference ? Code (CSharp): using System; using UnityEngine; [Serializable] public class A { public int a; } [Serializable] public class B : A { public int b; } [Serializable] public class C : A { public int c; } [Serializable] public class D<T> : A { public T d; } public class TestSerializeReference : MonoBehaviour { [SerializeReference] public A a = null; public D<int> d = new D<int>(); public bool changeType = false; private void OnValidate() { if (changeType) { if (a == null || a is D<int>) { a = new A(); } else if (a is C) { a = new D<int>(); } else if (a is B) { a = new C(); } else { a = new B(); } changeType = false; } } } I took a quick look at it recently and only got an editor crash when executing "a = new D<int>();". Hope it's juste an alpha bug and that it will be supported in the future.
This looks nice, tested a few cases. This will probably enable Unity to also mark the UnityEvent<T> classes as serializable and make them non abstract; so that we could serialize UnityEvent<bool> directly without creating a subclass first.
Yep, just found that too and reported it with a tiny repro. It crashes if any generic class is directly serialized with SerializeReference with compute_class_bitmap: Invalid type 13 for field Container`1[T]:Value @LeonhardP Case 1185214
All editor crashes are bugs that should be reported. It might not be that the feature is supposed to be supported, but nothing should crash the editor.
Are list of generic classes supposed to show up in the inspector? Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class TestSerialization : MonoBehaviour { [System.Serializable] public class D<T> { public T d; public T d2; } public List<D<int>> myclass = new List<D<int>>();// doesn't show up in the inspector' public D<int> myclass2; }
There's an open bug about that right now. The main issue is fixed but we're resolving one secondary issue about how it interacts with [SerializeReference].
It's probably the following issue, in case you want to keep track: https://forum.unity.com/threads/case-1188478-list-of-generic-type-not-serialized.754442/
Is there any plan to support generics for scriptable objects too? Knowing enough about how they work and comments about UnityEngine.Object not being supported together with SerializeReference this is probably a no.
In the below example, genericMonoA showed in inspector as an Object field(as expect), but genericMonoB was treated as a pure class with foldout in inspector, and will call new() on game start (prohibited for MonoBehaviour) Is this some kind of bug? Code (CSharp): using UnityEngine; [System.Serializable] public class GenericMono<T> : MonoBehaviour { [SerializeField] T t; } public class GenericMonoFloat : GenericMono<float> { } public class GenericBehaviour : MonoBehaviour { [SerializeField] GenericMonoFloat genericMonoA; [SerializeField] GenericMono<float> genericMonoB; }
Is there a way to serialise it manually and get the result? Similar to how JSONUtility works - it has the same constraints as the current serialisation used by Unity. Is there an equivalent for this new serialisation?
I'm not sure what you mean. This new behaviour is just an extension to the existing serialiser - it's not a whole new serialisation system.
This feature doesn't seem to work with arrays/lists of types that have a generic parameter. For example... Code (CSharp): [CreateAssetMenu(menuName = "Tables/Weapon")] public class WeaponTable : Table<Weapon> { } public abstract class Table<TValue> : ScriptableObject { public Row<TValue>[] rows; } [Serializable] public struct Row<TValue> { public TValue value; public int rating; } It works if table has just one row but not an array of them.
Are there any plans to implement default serialization for common data structure types from System.Collections.Generic (specially Dictionaries)?
Other than List<T>, which is already implemented, we have no concrete plans to implement serialization for other types in System.Collections.Generic. (We know it'd be nice).
Can you shed some light why there are no concrete plans to implement serialization for other types in System.Collections.Generic? Is it technical difficulties or just other things are more important?
Both. List<T> is easy to serialize because it's really just a wrapper around an array, but all the other types are more complicated, especially if we actually care about performance. I don't think it is impossible to solve - especially if we put some limitations on which generic parameters we allow - but it's not something that someone can just throw together in a weekend.
Though by not implementing it many users will make their own variant, which might perform even worse. And it would make shipping plugins much easier if there is a default unity implementation.
I don't think many people would care about anything else than Dictionary. I really can't see myself clamoring for a serializable HashSet or Stack. The big problem with the Dictionary is of course how to draw it. Serializing it is pretty trivial, although there might be some performance considerations I'm not seeing. But how do you make a view where it's easy to both change old relations and add new ones? How do you handle duplicate keys? etc.
It's cool. For perfection it would have to to serialize reference by interface. Consider this example: (IMonoBehaviour is fictional naturally) Code (CSharp): interface IGetComponent:IMonoBehaviour{} interface ISetComponent:IMonoBehaviour{} class GetSetComonent:MonoBehaviour,IGetComponent,ISetComponent{ } And then you could do in your code public ISetComponent setter; public IGetComponent getter;
Yeah, dictionaries are really ubiquitous when compared to other structures. Odin Inspector's views are pretty good https://twitter.com/devdogunity/status/857522495968219138?lang=en
I really wish Unity supported interface serialization. Not being able to drag and drop monos using their interface really complicates using interfaces in Unity.
I just came here to see if you guys finally made dictionaries serialisable, guess not. All those problems have been solved a long, long time ago. Unity devs seem to be the only people on the planet still scratching their heads about it. And BTW serialising an associative array is something one can do in a weekend (been done before). Also, like someone said, performance of the solution integrated in the engine can't be worse than what we are all doing right now (i.e. hacking around it). Oh well, maybe Unity 2030 will do this right.
Anyone know if this works for ScriptableObjects too? For example: Code (CSharp): public class Container<T> : ScriptableObject { //.. }
For a ScriptableObject and any other native object to be created in general you need a concrete class to be derived, otherwise there is nowhere for the generic parameter to be resolved. As such, I cannot think of any particularly great reason to need to serialize generic UnityEngine.Object references, what is the use case?
References to generic MBs/SOs should work in a22 or later. That said, as @AlkisFortuneFish said, scenarios where you can use this are limited - e.g. I think you won't be able to persist such objects into assets.
If references to generic SOs/MBs work, what about referencing them via interface? This would make code so much neater!
Totally different problem. A generic SO/MB is still guaranteed to be an SO/MB once you go far enough up the type hierarchy; an interface could be anything, which means we cannot know ahead of time whether it should get serialised by reference or by value.
add an attribute (or a flag on [SerializeReference]) that can be added to the field to let the dev specify that. e.g. Code (CSharp): [SerializeAsUnityObjectReference] public IFoo foo; // object field in the inspector, accepts both MB and SO implementing IFoo [SerializeReference] //2019.3 public IFoo bar = new Bar(); // serialized inside current SerializedObject, already implemented
That's not a good solution because the point is that we would want to serialise references to both unity objects and POCOs within the same field across different instances of the object.
The team’s contributions to Unity are much appreciated. JSON serialization is important. Although serialization to disk is an important use case, the primary use for JSON serialization is network transmission. With this in mind the Unity serializer is not all that useful. Compared to Newtonsoft.Json or System.Text.Json (Microsoft), the Unity serializer is not portable to servers. It doesn’t support dictionaries and it has a very light feature set by comparison. I think the existing Unity serializer should be retired in favor of System.Text.Json. System.Text.Json does not allocate garbage and it supports interfaces, generics, converters, and truly unrivaled performance. Did I mention no allocation and low memory footprint? Everyone wins by supporting Microsoft’s portable open source solution.
So I've been playing around with the generic serialization over the weekend and I like it alot so far. Currently I'm working on a generic PropertyDrawer for a List containing a struct<T>. I've managed to get the generic type argument for the struct using reflection, even creating an instance of a generic List matching the generic parameter (using System.Activator.CreateInstance). But... I can't seem to get the actual values from the List in a clean way. I can access their SerializedProperty, but do I need need to parse it like so: Code (CSharp): // note: list variable is the serializedProperty I want to get the value from switch (list.type) { case "string": { var val = list.stringValue; break; } case "float": { var val = list.floatValue; break; } // etc. } Because this does work but is very cumbersome seen as the amount of switch-cases becomes very large. My question is if I can somehow use the FieldInfo.GetValue(Obj) that can actually return the value regardless of object/primitive without so much boilerplate? If not could you consider implementing this? Some older forums posts also wish for this feature, but I couldn't get their workarounds to work properly thus I implemented this parsing switch.
Turns out I didn't need that much reflection to get my project working anyway. Still the FieldInfo.GetValue would kick ass. Anyways here is an open source Generic Serializable Dictionary with a native look and feel in 66 LOC + property drawer: https://github.com/upscalebaby/generic-serializable-dictionary
Any news on the above? I can't seem to find it in the issue tracker. This sort of thing will allow us to dynamically serialize any object on a field with a wrapper generic: Code (CSharp): [SerializeReference] public object anyObject = 32; // this does not serialize // ... public interface IObject {} public class Wrapper<T> : IObject { public T Value; public Wrapper(T value) { Value = value; } } [SerializeReference] public IObject anyObject = Wrapper(32); // this could serialize if it didn't crash the editor.
We had a similar issue, we worked around that with this: Code (CSharp): [RefType(typeof(ISomething))] Object m_something; ISomething Something => m_something as ISomething; Magic is done in property drawer for [RefTypeAttribute]: - allows dragging only interfaces in - when circle icon is clicked, object picker is opened with predefined search like this: t:Something t:OtherSomethingImplementation t:SomethingElse (all implementations of the interface on Object which can be found) Not ideal, but works. You can drag both MonoBehaviours and ScriptableObjects into the field. Serializing non-Object classes/structs was never the point of this (at least for us). Boilerplate-free solution, which @M_R suggests would be really nice: [SerializeAsUnityObjectReference] Although I'd prefer shorter name [SerializeObjectReference]
I create ScriptableObjects in the editor scripts to support Undo/Redo for custom generic data that has no better containers. This doesn't seem to be working in 2020.1.0b5: Code (CSharp): // Data.cs public class Data<T> : ScriptableObject { } // Test.cs [ExecuteInEditMode] public class Test : MonoBehavior { void Update() { // This returns null. Debug.Log(ScriptableObject.CreateInstance<Data<string>>()); } }
@Flying_Banana from all my experimentation (which was some time ago) you still need to have a non generic type to use with CreateInstance. You can just reference them in a generic type field.
I think the scenarios where this would be very useful is if you have a class Class FloatContainer:Container<float>{ } and another Class OtherFloatContainer:Container<float>{ }an then, in a Monobehaviour you could pass both types to a serialized reference like public Container<float> container;.
The Script Serialization documentation should be updated with the new SerializeReference and support for generic fields.
Question: I use serialized generics a LOT, but of course using the old way of a class "wrapper" for each generic type. If I want to start using serialized generics, I'd lose all of my serialized data right? Any way to avoid this?
Maybe, maybe not, I have't seen what the serialized data actually looks like. Try it on a class and see what happens. To be honest, I can't imagine it would lose the data.
Yep, as I thought, no difference in serialization: Code (CSharp): public class SomeScript : MonoBehaviour { public SomeClass<float> Parametrised; public FloatClass Derived; } [Serializable] public class SomeClass<T> { public T Value; } [Serializable] public class FloatClass : SomeClass<float> { } Code (Yaml): --- !u!114 &963194229 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 963194225} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 316cfc69e884d9446b42dcbb702cb2dc, type: 3} m_Name: m_EditorClassIdentifier: Parametrised: Value: 10 Derived: Value: 20