- Home /
Do Custom Inspectors Support Inheritance?
Let's say I have the following hierarchy set up:
BaseClass -> MyClass
If I make a custom inspector for BaseClass, can I get it to display for MyClass
?
Answer by sproinkalonk · Sep 01, 2013 at 02:44 PM
I'm not sure if/when this was changed, but you can explicitly tell Unity to use a particular Editor for inherited classes:
http://docs.unity3d.com/Documentation/ScriptReference/CustomEditor-ctor.html
e.g.
[CustomEditor( typeof( BaseClassName ), true )]
Hope this helps!
This does enable it to be used for derived classes, but how do you tell what derived class you have from the SerializedProperty?
not sure but you can check out the link above to find out
Answer by .sanders · Jan 20, 2012 at 07:11 PM
Well you can fix this easily by overwriting the OnInspectorGUI() in your BaseClass. If I were you I'd create a BaseClassEditor script which is the CustomEditor for the BaseClass and a MyClassEditor script which inherits from BaseClassEditor. To put it in your example:
// C#
[CustomEditor(typeof(BaseClass))]
public BaseClassEditor : Editor
{
public override void OnInspectorGUI()
{
// Put YOUR custom inspector code here for the base class
}
}
[CustomEditor(typeof(MyClass))]
public class MyClassEditor : BaseClassEditor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
// Additional code for the derived class...
}
}
This should work just fine. (not tested myself but am about to...) Hope this is what you're looking for...
Of course you NEED to define the [CustomEditor(typeof(MyClass))], if not unity will draw the default inspector even if you have a CustomEditor for the base class..
cheers!
I love this, can't test it right now myself, but please do tell when you tested it!
Does anyone know how I can replicate this behaviour with Property Drawers? I tried to but it acts like the child's OnGUI call is being ignored.
Did somebody encounter the problem of the OnInspectorGUI() function not getting called when implementing custom editors with inheritance like this? I do and altough the display of the custom inspector works the OnInspectorGUI() never gets called and so no changes are made to the corresponding variables although i changed them in the inspector.
@D$$anonymous$$asper_3DQR: I had to use serializedObject.Apply$$anonymous$$odifiedProperties(); to make my changes to the variables apply. Not sure if this is your issue though.
For me the mistake was that I unintentionally wrote the type of the editor into [CustomEditor(typeof()) because it almost has the same name as the class for which i wrote it. But I oversaw this several times.
Answer by whydoidoit · Dec 19, 2012 at 09:37 PM
I ran into this and you can make it work without having to write a custom inspector for every derived class - not sure how wise it is, but this works in U3/U4 - certainly totally undocumented :)
using UnityEngine;
using System.Collections;
using UnityEditor;
using System;
using System.Reflection;
using System.Linq;
[InitializeOnLoad] //Make sure this code runs every time the editor is updated
public class MagicInspectors : EditorWindow {
static MagicInspectors()
{
//Get access to the UnityEditor assembly
var asm = Assembly.GetAssembly(typeof(UnityEditor.CustomEditor));
//Use Linq to find the CustomEditorAttribute type
var cea = asm.GetTypes().FirstOrDefault(t=>t.Name == "CustomEditorAttributes");
//Get access to the method that is called to find a custom editor for a type - this
//caches the results, so it has to happen before we play with the lists
var findCustomEditor = cea.GetMethod("FindCustomEditorType", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
//Call it
findCustomEditor.Invoke(null, new object [] { new UnityEngine.Object(), false });
//Find the MonoEditorType class so we can make instances of it later
var next = asm.GetTypes().FirstOrDefault(t=>t.Name .Contains("MonoEditorType"));
var inst = Activator.CreateInstance(next);
//Get the field in that class which is the type of the inspector to use
var inspectorType = next.GetField("inspectorType");
//Get the field in that class which is the type to inspect using this inspector
var inspectedType = next.GetField("inspectedType");
//Get the custom editors field which is the cache in CustomEditorAttribute
var editorsField = cea.GetField("m_CustomEditors", BindingFlags.Static | BindingFlags.NonPublic);
//Get the current list (it's an ArrayList)
var editors = editorsField.GetValue(null) as ArrayList;
//Get the current list of multi item editors
var multiEditorsField = cea.GetField("m_CustomMultiEditors", BindingFlags.Static | BindingFlags.NonPublic);
var multiEditors = multiEditorsField.GetValue(null) as ArrayList;
//Now its time to get all of the inspectors we've defined
//Get all of the current assemblies loaded
var types = AppDomain.CurrentDomain
.GetAssemblies()
//Get all of the types in those assemblies
.SelectMany(a=>a.GetTypes())
//Which have a CustomEditor attribute
.Where(t=>t.IsDefined(typeof(CustomEditor), true))
//Get the type that this CustomEditor edits
.Select(t=>new { editor = t, inspected = (Attribute.GetCustomAttribute(t, typeof(CustomEditor), false) as CustomEditor).m_InspectedType}).ToList();
//Now look for types that are the type edited or its subclasses
var usableTypes = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(a=>a.GetTypes())
//For all of the types, get the custom inspector for which they are assignable
//In other words find an inspector (or null) for which this type can be downcast to
.Select(t=>new { editable = t, custom = types.FirstOrDefault(e=>e.inspected.IsAssignableFrom(t)) })
//Make sure we only have valid ones
.Where(r=>r.custom != null);
//Now update the internal cache with these new types
foreach(var newEditor in usableTypes)
{
//Create a new instance of the internal structure that represents the relationship
var editorInstance = Activator.CreateInstance(next);
//tell it which inspector to use
inspectorType.SetValue(editorInstance, newEditor.custom.editor);
//tell it which type to inspect from the list we made above
inspectedType.SetValue(editorInstance, newEditor.editable);
//Add it to multiEditors if it supports it
if(newEditor.custom.editor.GetType().IsDefined(typeof(CanEditMultipleObjects), false))
multiEditors.Add(editorInstance);
//Add it to ordinary editors always
editors.Add(editorInstance);
}
}
}
I have no idea what this code is doing, but it works. This is the solution to this problem, as far as I can see. I'd upvote it, but I'm a mere peasant on this site.
This looks like very interesting code. There are some methods you use of which I'm not entirely sure what they do. Would you $$anonymous$$d adding some comments to explain what's happing? Thanks!
Sure :) I can do that -
Basically what it is doing is using the messing with the list of Editors Unity has already figured out for a project and adding the ones where the type is derived from an editable type.
Lines 17 & 19 in the code are forcing Unity to do its caching before we mess with the results! Internally Unity does this only once per load of the code - so if it's done it - it won't again. This class is marked InitializeOnLoad so it will be called immediately the editor is loaded or updated.
Internally Unity has a $$anonymous$$onoEditorType class that associates a class with a custom editor - as we all know that only happens for specific types and not their derivatives. So we go ahead, scan all the types, find the derivatives and add them as well.
To do that we have to access some classes that are marked as internal, so a lot of the code is using reflection to do just that - if Unity changes the names of any of those internal properties or fields then it would break - they haven't done that in a while, so I'm hoping it's good for the long term.
(Note you can also use tricks like this with internal .NET classes. That technique lets my Unity Serializer save the current state of a coroutine and resume it later after the game has been reloaded from a server or player prefs etc etc)
I'll comment the code.
BTW I used DotPeek to look at how the existing Unity code works which let me find out the names and types of everything.
Answer by dago23 · Feb 08, 2012 at 07:10 PM
This doen't work in my case. I have a custom monobehaviour, let's say MyMonoBehaviour derived from MonoBehaviour. Users should be able to derive their new scripts from MyMonoBehaviour.
In the inspector I want to add some Buttons that belong to MyMonoBehaviour.
Problem is, I can not force users to write a custom inspector for their scripts and to call the base class inspector!
So is there any way to achieve my goal either? I don't see one at the moment. It would work if the default Inspector would call it's base class inspectors. But unfortunately it seems not to do that?
Any ideas?
Sure: remove the virtual keyword (you CAN also make it protected) from the definition of the OnInspectorGUI() so that your users can't override it and simply define a new virtual or abstract method which you call from the OnInspectorGUI() body and that users can override.. that way you guarantee that your inspector is called first and after that the additional content. Example:
[CustomEditor(typeof(BaseScript))] public abstract class BaseScriptEditor : Editor { protected abstract void OnDerivedInspectorGUI(); private void OnInspectorGUI() { // do YOUR custom stuff here OnDerivedInspectorGUI(); } }
Then you need to inform the users to override the OnDerivedInspectorGUI(). Of course you can't prevent the users from defining their own OnInspectorGUI() in this solution but when they first try to do that they will get a compiler warning that says it hides the OnInspectorGUI() of the base class (that's why it's protection level should be public or protected). So they will have some kind of notification. Of course they can remove the warning by adding the new keyword to their implementation but then they chose willingly to do so, at least they can't accidentally overwrite it or forget to call the base's OnInspectorGUI()
I don't think there is a more elegant solution to this problem than the one I mention here.
cheers
thanks, but that doesn't help in my case The private OnInspectorGUI is never called if it belongs to a base class.
And as I said, I don't want to force users to write an inspector.
hmm did not know that, thought it would work. If I have some time today I'll try some things out!
thanks again, but I think there is no way. Tried the whole day ;)
Answer by ematsuno · Mar 11, 2019 at 06:33 PM
it' 2019, my research corroborates the findings, It doesn't work out of the box as you would hope. The workarounds appear possible, but more complicated than worth the effort and probably less stable.
The compiler does not like this solution: [CustomEditor(typeof(BaseScript))] public abstract class BaseScriptEditor : Editor { protected abstract void OnDerivedInspectorGUI(); private void OnInspectorGUI() { // do YOUR custom stuff here OnDerivedInspectorGUI(); } } but it does look quite compelling, but the warnings bug me ;)
There are alternative ways to make it customizable as well, Copy/paste seems more stable for the kind of complexity I need at the moment unless there's a better solution...
Your answer
