I’m currently working on something like a custom variable management system.
I tried to make it as modular as possible, since I want to be able to scale the system in two different ways:
- which datatypes I can represent with the system (e.g. bool, string, int and later on custom types)
- How the effective value of the underlying datatype is stored… e.g. as a raw field, in a ScriptableObject, in a dictionary with an according key etc.
Therefor I have the following class, which I want to reference to in MonoBehaviours later on (the generic gives the scalability for datatypes)
[System.Serializable]
public abstract class BaseVariable<T>
{
[SerializeField] private Variable<T> _variable;
public void OnGUI(Rect position)
{
... // Doing some stuff on how to store the variable
_variable.OnGUI(position);
}
}
WIth the corresponding class Variable. Again, the generic is the datatype I want to represent. The class Variable itself is abstract and can later on be implemented in different ways, as long as it can give me access to a value of the specified type. Those different implementations guarantee my scalability for the storage and access:
[System.Serializable]
public abstract class Variable<T>
{
public abstract T Value {get; set;}
public abstract void OnGUI(Rect position);
}
Those two classes have the following implementations. The datatype I want to store for now is bool and the storage / access of the value is a simple field which gets accessed by the required Value property:
[System.Serializable]
public class BoolVariable : BaseVariable<bool>
{ }
[System.Serializable]
public abstract class BaseVariableRaw<T> : Variable<T>
{
[SerializeField] private T _value;
public override T Value
{
get => _value;
set => _value = value;
}
}
[System.Serializabe]
public class BoolVariableRaw : BaseVariableRaw<bool>
{
public override void OnGUI(Rect position)
{
Value = UnityEditor.EditorGUI.Toggle(position, Value);
}
}
and to inspect everything I use a simple MonoBehaviour which holds my BaseVariable:
public class BoolVariableTester : MonoBehaviour
{
[SerializeField] private BoolVariable _boolVariable;
}
which will call the following PropertyDrawer:
public abstract class BaseVariablePropertyDrawer<T> : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
UnityEngine.Object target = property.serializedObject.targetObject;
BaseVariable<T> instance = fieldInfo.GetValue(target) as BaseVariable<T>;
DrawInstance(position, instance);
}
public static void DrawInstance(Rect position, BaseVariable<T> instance)
{
instance.OnGUI(positon);
}
}
[CustomPropertyDrawer(typeof(BoolVariable))]
public class BoolVariablePropertyDrawer : BaseVariablePropertyDrawer<bool>
{ }
Ooookay, so far so good. This system works with different kinds of datatypes and different kinds of implementations. I created such implementations for my bool datatype. I designed my OnGUI() method in BaseVariable so I can select between different implementations and then write them into the “Variable _varialbe” field of my “BaseVariable” class. In this case I only showed the implementation of the RawVariable, but there are (potentially) other access types too.
But now I have the problem that I can’t serialize that instance. I can’t even serialize my instance of BoolVariable in my BoolVariableTester class.
Any suggestions how I can make this work without writing my own serializer listening to ISerializeCallbackReceiver? I know I can use ScriptableObjects to some degree to serlialize abstract classes and even keep references to same instances, but I can’t seem to figure out how to exactly apply those techniques to my special case with multiple levels of abstraction.
I’d be really glad if somebody could help me with this!
Thanks in advance,
BOTHLine