今天无意间发现了一篇好文章,也让我解决了一个很久都没解决的难题。问题是这样的,假如我想去拓展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()方法以后,原有的布局都就变了。
为什么会影响到原有布局呢?原因是这样的上面的代码是继承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
如下图所示,现在既保留了原有的布局,也可以方便的拓展了。。
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
- 本文固定链接: https://www.xuanyusong.com/archives/3931
- 转载请注明: 雨松MOMO于 雨松MOMO程序研究院 发表
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!



一看16年的文章了,还是能学到东西,代码已经收录。雨松加油,希望有更多的文章分享。
自定义这个组件的显示之后Scene窗口下锚点的4个小花瓣不显示,怎么让他显示出来
UGUI的类改不了啊,比如image
我使用4.6.1版本,想拓展PolygonCollider2D组件,只要[CustomEditor(typeof(PolygonCollider2D))]它,它上面的Edit Collider按鈕都不见了导致无法在场景编辑器上编辑。使用你说的方法在5.5版本是可用的,但是因为我需要导出古老的flash版本,所以只能用低版本,而低版本没有DecoratorEditor中的ReloadPreviewInstances方法覆盖,注释后导致unity崩溃,请问有办法修改能让DecoratorEditor这个类支持低版本吗
MOMO大师,你好,你的“UGUI研究院之UI粒子特效自适应缩放(二十二)”这篇文章下评论框出问题了,不能评论。 链接“https://www.xuanyusong.com/archives/4271”
在网上看了很多都是关于 Inspector 面板的编辑 却没有关于 Project 和 Hierarchy 或者其它 面板的编辑或者重写。想知道是否有方法可以?我想将project面板里面的部分资源按需求显示到自己创建的窗口当中。
这个得自己写一个窗口了 比较复杂了
再问一个问题,Inspector 面板下面的预览窗口是用什么创建的呢?
继承ObjectPreview 或者你在我微博里搜索preview关键字
是个Unity新手, 最近学编辑器拓展方面的知识.在拓展编辑器的时候遇到一个问题想请教一下, 就是一般的变量都可以通过EditorGUILayout.ObjectField()方法在inspector面板上绘出.可是UnityEvent变量应当如何处理, 也就是如何实现UGUI的那种事件拖拽框. 给个方向也行, 拜谢.
老师请问下 那个地图编译器工具 怎么做的
像对transform组件进行 坐标重置 功能一样,但在对RectTransform组件进行编辑时会有很多报错显示
另外serializedObject.FindProperty(“m_LocalPosition”);“m_LocalPosition”是获取组件的localPosition属性那么 如何查到不同组件不同属性对应的字符索引呢?比如 recttransform的sizeData所对应的因为。。。。好多报错是null 找不到
https://www.xuanyusong.com/archives/4018
哥啊 我的意思是: mPos = serializedObject.FindProperty(“m_LocalPosition”);这是获取物体的transform——localPosition属性的 而我现在用同样的方式 获取rectTransform的localPosition时,有z轴变化了所以我想问的是 是不是这个”m_LocalPosition”我写错了,或者说 获取一个组件属性的索引的命名规则是什么?m_前缀吗?因为我是半路出家
我一直在想办法扩展Macanim State Machine的界面。但我试了只能通过statemachinebehaviour来进行增加和界面的项目。因为statemachinebehaviour是继承自ScriptableObject的,所以没法把场景hierarchy里面的gameobject拖进statemachinebehaviour的脚本的变量那里。但假如我有个需求,需要把gameObject里面的参数放到statemachinebehaviour的脚本那里,随着状态机的状态切换来修改GameObject的参数。这样就可以通过界面的方式来控制在状态切换的时候所修改的参数了。有办法实现么?
我想用这个方法扩展原有的transdform组件.但是会导致unity卡死,不知道是什么问题
啊 不会啊 我这里测试没问题。 我是unity5.3.3的版本
5.3.4,也是这样的,能出个可拓展的组件的列表就好了~~~
我也出现了相同的情况,同样5.3.3。
如果我想实现快捷增加组件,比如base.OnInspectorGUI ();
if(GUILayout.Button(“Adding this button”))
{
//添加button组件
}
应该怎么做?
如果我想实现快捷增加组件,比如base.OnInspectorGUI ();
if(GUILayout.Button(“Adding this button”))
{
//添加button组件
}
应该怎么做?
感谢分享~
[给力][赞][赞][赞][给力][给力][给力]