首页 >Unity3D频道 >【Unity3D拓展编辑器】 > Unity3D研究院编辑器之不影响原有布局拓展Inspector(二十四)
2016
03-26

Unity3D研究院编辑器之不影响原有布局拓展Inspector(二十四)

今天无意间发现了一篇好文章,也让我解决了一个很久都没解决的难题。问题是这样的,假如我想去拓展Unity自带的inspector但是并不想影响原有布局。 比如下面这段代码:

[CustomEditor(typeof(RectTransform))]
public class MyTest : Editor 
{
	public override void OnInspectorGUI ()
	{
		base.OnInspectorGUI ();
		if(GUILayout.Button("Adding this button"))
		{
			Debug.Log("Adding this button");
		}
	}

}

我的本意是想在Rect Transform面板的下面去添加一个按钮,可是我一旦调用base.OnInspectorGUI()方法以后,原有的布局都就变了。

Snip20160326_1

 

为什么会影响到原有布局呢?原因是这样的上面的代码是继承Editor的,那么base.OnInspectorGUI()实际上去掉用了Editor类里的OnInspectorGUI()方法,可是RectTransfm的OnInspectorGUI()方法是在RectTransformEditor这个类写的。

但是问题就来了,RectTransformEditor这个类不是一个对外公开的类。所以不能继承它,那也就无法调用它的OnInspectorGUI()方法了,所以就有了上述问题。

这里有一个巧妙的反射方法,完美的解决这个问题。https://gist.github.com/liortal53/352fda2d01d339306e03

[CustomEditor(typeof(RectTransform))]
public class MyTest : DecoratorEditor
{
	public MyTest(): base("RectTransformEditor"){}
	public override void OnInspectorGUI ()
	{
		base.OnInspectorGUI ();
		if(GUILayout.Button("Adding this button"))
		{
			Debug.Log("Adding this button");
		}
	}
}

理论上unity提供的每一个脚本都有一个 XXXEditor 类 , 用来绘制它的面板。(本文用到的就是 RectTransformEditor)如果你不确定可以去我反编译的代码里面去找。https://bitbucket.org/xuanyusong/unity-decompiled

如下图所示,现在既保留了原有的布局,也可以方便的拓展了。。

Snip20160326_5

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 ();
	}
}

版本: Unity5.3.3

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

Unity3D研究院编辑器之不影响原有布局拓展Inspector(二十四)》有 24 条评论

  1. WongPing说:

    一看16年的文章了,还是能学到东西,代码已经收录。雨松加油,希望有更多的文章分享。

  2. oucfeng说:

    自定义这个组件的显示之后Scene窗口下锚点的4个小花瓣不显示,怎么让他显示出来

  3. 竹林说:

    UGUI的类改不了啊,比如image

  4. kingBook说:

    我使用4.6.1版本,想拓展PolygonCollider2D组件,只要[CustomEditor(typeof(PolygonCollider2D))]它,它上面的Edit Collider按鈕都不见了导致无法在场景编辑器上编辑。使用你说的方法在5.5版本是可用的,但是因为我需要导出古老的flash版本,所以只能用低版本,而低版本没有DecoratorEditor中的ReloadPreviewInstances方法覆盖,注释后导致unity崩溃,请问有办法修改能让DecoratorEditor这个类支持低版本吗

  5. 冬晓说:

    MOMO大师,你好,你的“UGUI研究院之UI粒子特效自适应缩放(二十二)”这篇文章下评论框出问题了,不能评论。 链接“https://www.xuanyusong.com/archives/4271”

  6. 秦海刚说:

    在网上看了很多都是关于 Inspector 面板的编辑 却没有关于 Project 和 Hierarchy 或者其它 面板的编辑或者重写。想知道是否有方法可以?我想将project面板里面的部分资源按需求显示到自己创建的窗口当中。

  7. 是个Unity新手, 最近学编辑器拓展方面的知识.在拓展编辑器的时候遇到一个问题想请教一下, 就是一般的变量都可以通过EditorGUILayout.ObjectField()方法在inspector面板上绘出.可是UnityEvent变量应当如何处理, 也就是如何实现UGUI的那种事件拖拽框. 给个方向也行, 拜谢.

  8. 老师请问下 那个地图编译器工具 怎么做的

  9. 羊咩咩说:

    像对transform组件进行 坐标重置 功能一样,但在对RectTransform组件进行编辑时会有很多报错显示

  10. 我一直在想办法扩展Macanim State Machine的界面。但我试了只能通过statemachinebehaviour来进行增加和界面的项目。因为statemachinebehaviour是继承自ScriptableObject的,所以没法把场景hierarchy里面的gameobject拖进statemachinebehaviour的脚本的变量那里。但假如我有个需求,需要把gameObject里面的参数放到statemachinebehaviour的脚本那里,随着状态机的状态切换来修改GameObject的参数。这样就可以通过界面的方式来控制在状态切换的时候所修改的参数了。有办法实现么?

  11. 罗启豪说:

    我想用这个方法扩展原有的transdform组件.但是会导致unity卡死,不知道是什么问题

  12. 邓哞哞说:

    如果我想实现快捷增加组件,比如base.OnInspectorGUI ();
    if(GUILayout.Button(“Adding this button”))
    {
    //添加button组件
    }
    应该怎么做?

  13. 邓哞哞说:

    如果我想实现快捷增加组件,比如base.OnInspectorGUI ();
    if(GUILayout.Button(“Adding this button”))
    {
    //添加button组件
    }
    应该怎么做?

  14. 水岸上说:

    感谢分享~

  15. c6u2说:

    [给力][赞][赞][赞][给力][给力][给力]

留下一个回复

你的email不会被公开。 必填项已用 * 标注。