Hey I have been trying for a while now but cant seem to get Unity to recognize my generic class in the inspector. Can someone please give me an example on how this would be done in this case below? :
The Class:
[System.Serializable]
public class TableItem<TKey, TValue> {
public TKey Key = default(TKey);
public TValue Value = default (TValue);
public TableItem(TKey Key, TValue Value){
this.Key = Key;
this.Value = Value;
}
}
What I Have For A Property Drawer So Far (I’m arbitrarily using <string, string> until I can get it working) :
[CustomPropertyDrawer(typeof(TableItem<string, string>))]
public class tableItemPropertyDrawing :PropertyDrawer {
public void OnGUI(Rect pos, SerializedProperty prop, GUIContent label){
Debug.Log (true);
}
}
You cannot serialize a generic class. You CAN serialize a type derived from a generic class.
So instead of TableItem<string, string>, you can try:
// this derived type should serialize.
// the actual name of the class can be whatever you want
[System.Serializable]
public class StringTableItem : TableItem<string, string> { }
// we don't need an implementation, it's just to "wrap" our generic type
// in a concrete type that unity can serialize.
// the CustomPropertyDrawer should also use the concrete type too, I believe
It’s mildly annoying, but the reasons for it have to do with how the low-level unity serailizer works (and serializing generics is a complex problem on it’s own).
You can take a look at how to make your own UnityEvent<> types to see how this works in practice.
Here’s an example based on some of the ways I’ve used them (outside of UI callbacks, where the usage is obvious):
// A basic UnityEvent will serialize automatically, as it lacks any generic parameters.
// You can still wrap it in an empty sub-class to give it special meaning or make it's use clearer:
[System.Serializable]
public class LoadingFinished : UnityEvent { }
// Generic Unity events can be wrapped as in the example I gave earlier:
[System.Serializable]
public class StuffLoadedWithTotalTime : UnityEvent<UnityEngine.Object[],float> { }
// here's how you would use them:
public class Loader : MonoBehaviour
{
// public serialization, less encapsulated:
public LoadingFinished OnLoadingFinished = new LoadingFinished();
// non-public serialization, more encapsulated:
[SerializeField]
private StuffLoadedWithTotalTime m_onStuffLoaded = new StuffLoadedWithTotalTime();
public StuffLoadedWithTotalTime OnStuffLoaded
{
get { return m_onStuffLoaded; }
}
/* you could wrap the proceeding example with add/remove property handlers
* for the listener AddListener/RemoveListener functions, and make it look
* just like a normal C# event delegate to outside classes.
*/
public IEnumerator Load(params UnityEngine.Object _stuff[])
{
var start_time = Time.realtimeSinceStartup;
// loading stuff...
var total_time = Time.realtimeSinceStartup - start_time;
m_onStuffLoaded.Invoke(_stuff, total_time); // for the things that care what was loaded and how long
OnLoadingFinished.Invoke(); // for the things that don't
}
}
This makes it easy to get your own editor-exposed callback framework for custom systems.