As luck would have it, the FieldInfoPropertyDrawer type's fieldInfo has a handy DeclaringType property that tells us exactly where itthe field was defined, without needing to walk the inheritance hierarchy ourselves - at least for fields directly on the object we're inspecting. And PropertyDrawers already have accessAs we discovered in the comments, we still need to that via their fieldInfo memberdo a bit of manual walking for fields in a child object or struct. :)
using UnityEngine;
using UnityEditor;
[CustomPropertyDrawer(typeof(HideInDerivedAttribute))]
public class HideInDerivedDrawer : PropertyDrawer {
bool? _chachedIsDerived;
// Cache this so we don't have to muck with strings
// or walk the type hierarchy on every repaint.
bool IsDerived(SerializedProperty property) {
//if Get(_chachedIsDerived.HasValue the== typefalse) of{
the object we're editing string path = property.propertyPath;
var type = property.serializedObject.targetObject.GetType();
if (path.IndexOf('.') > 0) {
// Is thisField fieldis fromin a morenested ancestraltype. Dig down to get that type.
than the one we're drawing? var fieldNames = path.Split('.');
return for(int i = 0; i < fieldNames.Length - 1; i++) {
var info = type.GetField(fieldNames[i]);
! if (info == null)
break;
type = info.FieldType;
}
}
_chachedIsDerived = fieldInfo.DeclaringType;DeclaringType != type;
}
return _chachedIsDerived.Value;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
// Abort ifIf we're in a more derived type than where this field was declared,
// abort and draw nothing instead.
if (IsDerived(property))
return;
return;
// Otherwise, draw the control as we normally would.
EditorGUI.PropertyField(position, property, label, true);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
if (IsDerived(property)) {
// Collapse the unseen derived property.
return -EditorGUIUtility.standardVerticalSpacing;
} else {
// Provision the normal vertical spacing for this control.
return EditorGUI.GetPropertyHeight(property, label, true);
}
}
}
Known issues
This does not stack with other property drawing attributes like
[Range(min, max]. Only one attribute will take effect, depending on their order. Internally it seems Unity tracks only one inspector attribute per field.It may be possible with some heavy-duty reflection to identify other attributes on the field with their own CustomPropertyDrawers, and construct a corresponding PropertyDrawer to delegate the drawing to, but it would get messy fast. It might be simpler to make a combined attribute like
[HideInDerivedRange(min, max)]for specific cases.Types with their own custom property drawer will fall back to the default drawing style if marked
[HideInDerived]. It works correctly for all of Unity's built-in property widgets though, showing colour pickers and vector/object fields as expected.This does not correctly hide the label, fold-out, and size controls of an Array or List, although it does hide the collection's contents.
As Candid Moon found, this is due to a change in Unity 4.3 to allow Attribute-targeted PropertyDrawers to apply to each element in a collection, which unfortunately makes it impossible to target the array itself in the present version.
A workaround suggested by slippdouglas here is to define a serializable struct containing your collection, so that the
[HideInDerived]attribute hides the struct as a whole, like so:
Unfortunately.
[System.Serializable]
public struct HideableStringList {
public List<string> list;
// Optional, for convenience, so the rest of your code can
// continue to behave like this is just a List<string>
// without always referencing the .list field.
public static implicit operator List<string>(HideableStringList c) {
return c.list;
}
public static implicit operator HideableStringList(List<string> l) {
return new HideableStringList(){ list = l };
}
}
Because of the way Unity draws the inspector, I haven't foundwe can't use a waysingle generic type to consistently get this playing nicely with other PropertyDrawer attributeshandle all these cases, like if you wanted a [Range]so you'll have to both show with a slider control in the base class and be hidden in the derived class. So far I can only get one or the othercopy this boilerplate for each array type (yuck). But at least it lets this work:
[HideInDerived]
HideableStringList myStrings = new List<string>(3);