#Possible Bug related to Serialising Generics in Unity

1 messages · Page 1 of 1 (latest)

reef dove
#

Has anyone ever experienced a bug where you serialise a Scriptable Object field with [SerializeField] and it shows up in the inspector as a field but when trying to assign an instance to it it just remains with the "None" (no object assigned) reference?

In light of the above, I might say that my Scriptable Object is a Generic one; however, I do not create instances of it but of an inherited class of it which defines the type so that Unity should be happy. In the class that has the serialised field, I say that I want an ScriptableObject<object> (Where it says Scriptable Object, there goes the name of my actual class not the Unity base one) instance. How should I present this field if the object constraint is what makes happen the issue?

languid hearth
#

does unity even support this? not sure what version you use but, for serializing generic objects, I'm used to making a subclass like
[Serializeable]
public class SpecificImplementation : BaseGenericClass<Type> {}
and what you describe sounds like this is already done but maybe the serialized variable type also needs to have SpecificImplementation as type?

but if the field shows in the inspector for you, it sounds like it should be supported (?)
you could also try to look into https://docs.unity3d.com/ScriptReference/SerializeReference.html but not sure if that helps

#

actually looking into it, SerializeReference seems to only be for custom structures that are not inheriting from UnityEngine.Object

reef dove
#

Hi, thanks for such an early response.
Indeed, that is the way I use to make it appear, and then on the other script I store a

[SerializeField] BaseGenericClass<object> obj;
#

But I have also tried SerializeReference in case that worked without success

#

2021.3.f1 version, the almost latest LTS

languid hearth
#

does it work if the type is "just" ScriptableObject?

reef dove
#

What you say about the serialised variable also needing to have an implementation by unity is a bit complicated for me. See, this base class will only need to be specified with either int or float and no more. Which are of course supported

reef dove
languid hearth
#

hmmhh yeah, a more elegant solution that doesn't need individual checks and casting would be better,
because with this you could just check if the type of the serialized object is either the generic float or int object type and then cast.
do you have a minimal example for how your generic class looks and how it is used?
like what's the use case to either have int or float as type?

reef dove
#

It's quite trivial

#
public class AttackDataObject<THealthUnit> : ScriptableObject
{
    [field: SerializeField] public THealthUnit Damage { get; protected set; }
    [field: SerializeField] public LayerMask LayerMask { get; protected set; }
    [field: SerializeField] public bool AreaOfEffect { get; protected set; }
    [field: SerializeField] [field: Min(0f)] public float AreaDamageRadius { get; protected set; }
    [field: SerializeField] [field: Min(0)] public int MaxAreaDamageTargets { get; protected set; }
    [field: SerializeField] public THealthUnit AreaDamage { get; protected set; }
}
#

Just need then for the damage

languid hearth
#

why not just use float?

reef dove
#

To then distinguish when for instance making health bars or health "bits"

languid hearth
#

like if you consume this method, isn't that a bit tricky to handle too or not? if you have float and int cases?

so the question is more a presentation problem than a logical problem? because if you apply the same logic and the difference is just the unit/type, maybe you can use a different indicator for how to display a bar or bits

#

who is responsible for the displaying?

#

class wise

reef dove
#

Like today is when I decided to refactor my whole code because initially almost everything was generic with this type to better support it but it was just a mess as I would had needed to create a duplicate empty implementation of every single final class to make unity happy. Then it struck me that most classes didn't need to care about knowing the type and just passing it through, hence they would just hold a reference to the object generic and thankfully everything inherits and can be encapsulated with it

reef dove
languid hearth
#

I am wondering about the intention of this, because if it's purely to have a different presentation (and I think it would be easier/more convenient to just use floats anyways, since it is probably unlikely to come within a value area where floats are too inaccurate).
and if that's the case, I would work with different health bar visualization strategies

reef dove
#

Sorry, I might be too much of a fan of abstraction haha

languid hearth
#

for example it could be a base scriptable object, maybe abstract but not generic, with a method that instructs how to visualize the heath bar. and you have different sub classes for different implementations

or maybe you just have a slot for a prefab reference for the health visualizer.
and the top level object of the prefab has a MonoBehaviour that implements an interface for visualization, and you just get that with GetComponent after instantiating it and pass your health data or something

#

and not quite related, but if you're a fan of abstraction, a while ago i wrote about strategy pattern in unity and about a neat messenger system I use for decoupling logic,
you are probably familiar with certain concepts, but if you're interested

messenger object, combining the mediator and the observer pattern:
#1029620089554751498 message

strategy pattern with Unity MonoBehaviours
#old_programming message

reef dove
#

Sorry, before I dc'd but I will definitely check out those resources and inform should I happen to find a solution

reef dove
#

Hey, given our generics classes of above which inherit from ScriptableObject, should this expression return false in C#?

[SerializeReference] ScriptableObject _rawData;

bool Foo()
{
return _rawData is BaseDataClass<object>; //I assigned it from inspector to SpecificDataClass which : BaseDataClass<int>
}
#

I tried it but it returns false, then there is no pattern matching to be done

languid hearth
#
if (_rawData is BaseDataClass<float> floatInstance) {
   floatInstance.SomeMethod();
}
if (_rawData is BaseDataClass<int> intInstance) {
   intInstance.SomeMethod();
}

was thinking more like this

#

using your approach probably won't work if the base types are generic int or float

#

also I would really just abandon the idea to separate int and float implementation

reef dove
#

The thing is that at this step I really don't need anything int-floaty related

reef dove
languid hearth
#

you tried it like my last message above?

reef dove
#

YES

#

So

#

whatever is Base<object> //False
whatever is Base<TheExactTypeThatIAssigned> //True

#

Thank you at least this time "worked"

reef dove
#

You know what? Screw it, health is totally a float

reef dove
#

Hey, gave a look to your links. Very interesting indeed. This exact project that I am working on ought to be the base of a multiplayer, so your approach with the first system you recommended could suit my necessities pretty well. Also the GetComponent<IInterface> is how I always do it in Unity, it is great that Unity supports that cool way of doing things

#

If you are curious still about how I am doing it now onwards:

public abstract class DataObject : ScriptableObject
{
  public abstract float GetDamage();
}

public class DataObjectInt : DataObject
{
  [SerializeField] private int _damage;

  public override float GetDamage() => _damage;
}

//Same goes for float variant