- Home /
Custom Editor (lack of) Polymorphism
I'm creating a custom editor for some scripts and I want to add some common functionality across all of them. Something like:
public abstract class Animal : MonoBehaviour { }
public class Dog : Animal {}
public class Cat : Animal {}
Let's say I want to add an inspector button for each animal. I created a custom editor for the base class, but it won't inspect the children.
[CustomEditor(typeof(Animal))]
public class AnimalInspector : Editor {
public override void OnInspectorGUI () {
//Draw some buttons or something.
}
}
The problem is that Unity doesn't use the custom inspector if I inspect a derived class such as Dog
, and I don't want to create copies of the same editor script just to add the same feature to two different classes.
Any Suggestions, Peter G
Well, you could derive two new custom editors that inherit from your "base" AnimalInspector class.
I think the problem is that Unity would have trouble to find the "right" custom editor for a class. I guess Unity just uses typeof() to get the type of the inspected class and searches for a class with the CustomEditor attribute that matches the type.
If you would derive your class from more than one source (some interfaces) which inspector should Unity pick when (theoretically) all interfaces provide a custom inspector.
Thanks, I'm not sure then if its a .Net problem or a Unity thing. I'm poking around the msdn to see what I can find:
Attribute parameters are restricted to constant (const) values of the following types:
System.Type
so that shouldn't be a problem. Now onto the const part:
A constant expression must yield a value of the target type, or of a type that can be implicitly >converted to the target type.
Derived class to base class fits that bill as well. Note this next bit returns true as it should
typeof(Animal).IsAssignableFrom(typeof(Dog));
I think the problem is it doesn't use IsAssignableFrom. What Unity does is just use typeof, and not support inheritance in any way. This is a Unity problem, not a .Net thing.
Answer by NickWalker12 · Jul 06, 2013 at 09:50 PM
[CustomEditor(typeof(Animal), true)]
Not sure if this feature existed when you asked the question, but to include children, you use the overloaded CustomEditor(inspectedType : System.Type, editorForChildClasses : boolean).
http://docs.unity3d.com/Documentation/ScriptReference/CustomEditor.CustomEditor.html
Thank you, and that's a fairly new feature. Introduced after I asked the question. :)
Still, it is correct answer now, i.e. anyone who finds this question now will agree that this answer is more valuable than the one marked as correct.
What a nice feature that is! I just created a derived class from 2DT$$anonymous$$ and it made me sad to realize that all of the editor niceness was no longer available. Now, with a simple boolean, everything went back to being nice again!
Answer by jonas-echterhoff · Jun 16, 2011 at 07:29 AM
Looking at the code, it appears that Unity indeed compares the exact type, thus not supporting Polymorphism for CustomEditors. You could file a bug on it, it's probably a simple change, but also probably not the most demanded feature.
For now, it seems you'll have to create a separate Editor class for each derived class - though you should not need to duplicate your scripts, why not have all the Editors derive from one base Editor class?
This seems to be the easiest solution for the time being. Thanks.
Answer by SteveFSP · Jun 16, 2011 at 09:46 PM
Here is a pattern that is suitable for many use cases, and isn't too difficult to maintain:
Continuing with your example: When you create your base custom editor, move all of its GUI draw code to a static method. Something like:
[CustomEditor(typeof(Animal))]
public class AnimalInspector : Editor {
public override void OnInspectorGUI () {
DrawInspectorGUI((Animal)target);
}
public static bool DrawInspectorGUI (Animal targ) {
// Draw some buttons or something.
// Use the return value to indicate that the editor was updated.
}
}
When you extend the base MonoBehaviour, you can implement it's custom editor as follows:
[CustomEditor(typeof(Dog))]
public class DogInspector : Editor {
public override void OnInspectorGUI () {
Animal.DrawInspectorGUI((Animal)target);
// Place Dog specific GUI elements here.
}
}
Answer by ckfinite · Jun 16, 2011 at 03:30 PM
Another thing you could do (in addition to jonas's answer) is to use generics and reflection to add the subclasses CustomEditor attribute. What you would need to do is redefine AnimalInspector to look like this:
public abstract class AnimalInspector<T> : Editor, where T : AnimalInspector<T>
{
//do generic Animal stuff
}
And a client class would look like this:
public class DogInspector : AnimalInspector<Dog>
{
//Do doggy stuff
}
Then, you would in a post build script reflect all classes that extend AnimalInspector, get typeof(T), then add the CustomEditor attribute there.
It is a bit roundabout, but I think it would work.
Another way you could do it is to recursively iterate over all subclasses in reflection. Then, when you found a subclass, add a CustomEditor attribute to the superclass. Again, annoying, but I think it could work.
Now its just deciding on the lesser of two workarounds. :) Its probably just going to come down to how many classes I want to custom inspect.
Very interesting. I'm probably going to start with Jonas's answer until the project gets too big for me to manually add new editor scripts. And a quick check to make sure I'm understanding you, I believe that you want DogInspector to inherit from AnimalInspector<DogInspector>
If not, then I think I'm missing your pattern some.
Shouldn't the constraint be of type Animal? Since you implement the DogInspector with Dog and Dog is not an AnimalInspector.
$$anonymous$$aybe you wanted to implement it like this:
public class DogInspector : AnimalInspector<DogInspector>
But I'm not sure it that works in C#. C++ templates should work that way ;)
edit Just tried it and it works ;) but i think it's pretty useless to use generics here. A normal base class and derived class should be enough.
Now i get why you want to use generics, but i don't think that would work. Reflection is a runtime feature (not compile-time). Unity checks the attributes after compilation so changing/adding them later won't work, i guess.
But i like the idea ;)
I did actually mean it to be
public class DogInspector : AnimalInspector<DogInspector>
but, as I thought about it, the implementation example is correct, so it really should be
public class DogInspector : AnimalInspector<Dog>
And it is it surprising with the constraint that it doesn't throw a compiler error.
The reason why generics are needed here is so that the postprocessor could generate the correct attribute for Unity. This can work at compile time, if the reflection script can be attached to a post-build event. if unity doesn't have that, then this won't work.
Answer by LightStriker · Sep 13, 2013 at 02:04 PM
I know it's an old question, but the newest version (4+) of Unity offer an alternative:
[CustomEditor(typeof(MyBaseClass), true)]
The second parameter is listed as "Editor For Child Classes".
However, it only works for direct hierarchy and interface implementation is not supported. (bleh)
Also, using typeof(UnityEngine.Object)
as base class is also not supported, assuming you would like your editor to show up on any kind of object.
Yeah, I ran into the same thing. I have tried with both Object and Component. All the component types inherit from the component class, but using the new "editor for child classes" does not seem to work on the component, so that you can actually add custom fields for all components. A bit annoying.
Its mainly because I want to make it possible to select and copy multiple components so they can be copied to another game object.
Your answer
Follow this Question
Related Questions
Empty space at the top of a custom property drawer layout? 2 Answers
How should I serialize data that is also editable in the Inspector? 2 Answers
Custom inspector for AnimatorStateTransition 0 Answers
Custom Editor for Monobehavior with custom property not retaining Gameobject references 0 Answers
Custom Inspector: Using SeralizedProperty changes the prefab values! 0 Answers