Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Extend Unity's built-in inspectors
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
/// <summary>
/// A base class for creating editors that decorate Unity's built-in editor types.
/// </summary>
public abstract class DecoratorEditor : Editor
{
// empty array for invoking methods using reflection
private static readonly object[] EMPTY_ARRAY = new object[0];
#region Editor Fields
/// <summary>
/// Type object for the internally used (decorated) editor.
/// </summary>
private System.Type decoratedEditorType;
/// <summary>
/// Type object for the object that is edited by this editor.
/// </summary>
private System.Type editedObjectType;
private Editor editorInstance;
#endregion
private static Dictionary<string, MethodInfo> decoratedMethods = new Dictionary<string, MethodInfo>();
private static Assembly editorAssembly = Assembly.GetAssembly(typeof(Editor));
protected Editor EditorInstance
{
get
{
if (editorInstance == null && targets != null && targets.Length > 0)
{
editorInstance = Editor.CreateEditor(targets, decoratedEditorType);
}
if (editorInstance == null)
{
Debug.LogError("Could not create editor !");
}
return editorInstance;
}
}
public DecoratorEditor (string editorTypeName)
{
this.decoratedEditorType = editorAssembly.GetTypes().Where(t => t.Name == editorTypeName).FirstOrDefault();
Init ();
// Check CustomEditor types.
var originalEditedType = GetCustomEditorType(decoratedEditorType);
if (originalEditedType != editedObjectType)
{
throw new System.ArgumentException(
string.Format("Type {0} does not match the editor {1} type {2}",
editedObjectType, editorTypeName, originalEditedType));
}
}
private System.Type GetCustomEditorType(System.Type type)
{
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var attributes = type.GetCustomAttributes(typeof(CustomEditor), true) as CustomEditor[];
var field = attributes.Select(editor => editor.GetType().GetField("m_InspectedType", flags)).First();
return field.GetValue(attributes[0]) as System.Type;
}
private void Init()
{
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var attributes = this.GetType().GetCustomAttributes(typeof(CustomEditor), true) as CustomEditor[];
var field = attributes.Select(editor => editor.GetType().GetField("m_InspectedType", flags)).First();
editedObjectType = field.GetValue(attributes[0]) as System.Type;
}
void OnDisable()
{
if (editorInstance != null)
{
DestroyImmediate(editorInstance);
}
}
/// <summary>
/// Delegates a method call with the given name to the decorated editor instance.
/// </summary>
protected void CallInspectorMethod(string methodName)
{
MethodInfo method = null;
// Add MethodInfo to cache
if (!decoratedMethods.ContainsKey(methodName))
{
var flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
method = decoratedEditorType.GetMethod(methodName, flags);
if (method != null)
{
decoratedMethods[methodName] = method;
}
else
{
Debug.LogError(string.Format("Could not find method {0}", method));
}
}
else
{
method = decoratedMethods[methodName];
}
if (method != null)
{
method.Invoke(EditorInstance, EMPTY_ARRAY);
}
}
public void OnSceneGUI()
{
CallInspectorMethod("OnSceneGUI");
}
protected override void OnHeaderGUI ()
{
CallInspectorMethod("OnHeaderGUI");
}
public override void OnInspectorGUI ()
{
EditorInstance.OnInspectorGUI();
}
public override void DrawPreview (Rect previewArea)
{
EditorInstance.DrawPreview (previewArea);
}
public override string GetInfoString ()
{
return EditorInstance.GetInfoString ();
}
public override GUIContent GetPreviewTitle ()
{
return EditorInstance.GetPreviewTitle();
}
public override bool HasPreviewGUI ()
{
return EditorInstance.HasPreviewGUI ();
}
public override void OnInteractivePreviewGUI (Rect r, GUIStyle background)
{
EditorInstance.OnInteractivePreviewGUI (r, background);
}
public override void OnPreviewGUI (Rect r, GUIStyle background)
{
EditorInstance.OnPreviewGUI (r, background);
}
public override void OnPreviewSettings ()
{
EditorInstance.OnPreviewSettings ();
}
public override void ReloadPreviewInstances ()
{
EditorInstance.ReloadPreviewInstances ();
}
public override Texture2D RenderStaticPreview (string assetPath, Object[] subAssets, int width, int height)
{
return EditorInstance.RenderStaticPreview (assetPath, subAssets, width, height);
}
public override bool RequiresConstantRepaint ()
{
return EditorInstance.RequiresConstantRepaint ();
}
public override bool UseDefaultMargins ()
{
return EditorInstance.UseDefaultMargins ();
}
}
@usernameHed
Copy link

usernameHed commented Sep 21, 2019

same
Same problem as Dennin, 3 years later, and no fix ? ;) on unity 2019

@shaunus84
Copy link

shaunus84 commented May 29, 2020

The argument exception error is due to Unity trying to serialise the same inspector, as it behaves as if two inspector windows are open. You can get around this by not implementing OnInspectorGUI in the DecoratorEditor.cs file but instead calling it through the CallInspectorMethod in a different function then calling this from either your extended class or anywhere within the call that the editor is making to draw GUI.

@CrowbarSka
Copy link

CrowbarSka commented Jun 24, 2021

Thanks for this helpful script!

I'm trying to override the GameObject editor and followed your tutorial but I keep getting "Could not find method" errors spammed in the console. I traced it back to: CallInspectorMethod("OnSceneGUI"); I commented this part out because it doesn't seem necessary I think? But just thought I'd let you know.

This is in Unity 2020.3.12f1.

@JingFengJi
Copy link

JingFengJi commented Apr 29, 2022

image
image

Unity Version 2020.3.9f1c1
Cannt use for RectTransform Component , the editorInstance is null

@Shohag503
Copy link

Shohag503 commented Jul 21, 2022

Verify Github on Galaxy. gid:bCBksuiNuPEUeaXeZkH2BC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment