Skip to content

Instantly share code, notes, and snippets.

@liortal53
Last active October 21, 2024 10:03
Show Gist options
  • Save liortal53/352fda2d01d339306e03 to your computer and use it in GitHub Desktop.
Save liortal53/352fda2d01d339306e03 to your computer and use it in GitHub Desktop.
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 ();
}
}
@CrowbarSka
Copy link

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

image
image

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

@Shohag503
Copy link

Verify Github on Galaxy. gid:bCBksuiNuPEUeaXeZkH2BC

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