Unity扩展开发中,除了常用的菜单等功能,剩下的就是开发编辑器实现自己工具的业务逻辑。总结Unity编辑器的实现方式,主要包括三种:
- 新建编辑器窗口
- 扩展Scene
- 扩展Inspector
最近在做Unity扩展工具相关工作。在此整理一下Unity扩展开发的一些编辑器扩展知识。方便大家以后能够快速构建自己的Unity编辑器。
新建窗口
实现自定义窗口需要如下步骤:
(1)实现窗口
实现窗口的代码框架如下所示:
public class NewWindow : EditorWindow {/// <summary>/// 初始化数据/// </summary>private void OnEnable(){}/// <summary>/// 实现界面绘制和显示逻辑/// </summary>private void OnGUI(){/** 实现显示界面如文章Unity Plugin开发之UI中介绍的UI实现方法*/GUILayout.Label("This is a new window");}/// <summary>/// 当窗口获得焦点时被调用/// </summary>private void OnFocus(){}/// <summary>/// 当窗口失去焦点时被调用/// </summary>private void OnLostFocus(){}/// <summary>/// 窗口被销毁时调用/// </summary>private void OnDestroy(){}}
其中每个方法的功能见注释所示。具体示例,这里不再演示,可以参考文章半小时学会Unity扩展开发之编辑器UI篇
的实现。
(2)调用、显示窗口
可以通过方法EditorWindow.GetWindow或GetWindowWithRect(该方法执行显示位置)实现窗口的显示。如下代码,当点击菜单时,实现窗口的显示。
public class CallNewWindow{[MenuItem("Tools/My Window")]private static void ShowWindow(){EditorWindow.GetWindow(typeof(NewWindow));}}
扩展Scene
在有些开发中,为了方便,可能需要对Scene进行定制。即对Unity提供的Scene Editor进行扩展,在原有Scene Editor上实现需要定制化的功能。
扩展Scene的过程可以概括为以下几步:
(1)继承SceneView
(2)实现对Scene Editor的定制
(3)将定制函数委托给SceneView
(4)显示定制内容
实现新建Scene过程如下代码片段所示。
[InitializeOnLoad]public class NewScene : SceneView{//存储Vector3坐标private static Vector3 vector3;static NewScene(){//委托显示自定义内容NewScene.onSceneGUIDelegate += DelegateShowScene;}private static void DelegateShowScene(SceneView sceneView){NewScene.OnShowOwnGUI(sceneView);}private static bool isBigger;private static bool isSmaller;/// <summary>/// 显示在Scene上的内容/// </summary>/// <param name="sceneView"></param>private static void OnShowOwnGUI(SceneView sceneView){vector3 = Camera.main.transform.localScale;//开始绘制GUIHandles.BeginGUI();GUILayout.BeginArea(new Rect(400, 350, 300, 400));GUILayout.BeginHorizontal();isBigger = GUILayout.Button("MainCamera变大");isSmaller = GUILayout.Button("MainCamera变小");GUILayout.EndHorizontal();if (isBigger){Camera.main.transform.localScale = new Vector3(vector3.x * 2, vector3.y * 2, vector3.z * 2);}if (isSmaller){Camera.main.transform.localScale = new Vector3(vector3.x / 2, vector3.y / 2, vector3.z / 2);}EditorGUILayout.Space();Vector3 vector3Now = Camera.main.transform.localScale;EditorGUILayout.LabelField("Main Camera scale = (" + vector3Now.x + "," + vector3Now.y + "," + vector3Now.z + ")", GUIStyle.none);GUILayout.EndArea();Handles.EndGUI();}}
定制化后的Scene如下图所示,从图中可以看出,在函数OnShowOwnGUI中绘制的Button和Label都正确的显示在了Scene上。
扩展Inspector
有时候,我们需要扩展现有的Inspector,实现自己的需求。这里将介绍两部分,首先介绍如何实现自定义数据在Inpsector上显示的功能;然后会介绍如何实现对现有Inspector的扩展。
实现自定义Inspector
如下代码实现了一个简单的自定义Inspector,实现的功能是“当a的值为true时,在Inspector上显示b;当b的值大于0.5时,在Inspector上显示c”。代码如下所示。
(1)MyData.cs代码:
using UnityEngine;public class MyData :MonoBehaviour{//这些public型的数据会被序列化public bool a;public float b;public int c;}
(2)ExtendInspector.cs代码如下(实现Inspector显示功能):
using UnityEditor;[CustomEditor(typeof(MyData))]public class ExtendInspector : Editor {// 存储MyData中的a、b、c被序列化后的对象public static SerializedProperty a_prop, b_prop, c_prop;void OnEnable(){// 获得MyData对应的序列化值a_prop = serializedObject.FindProperty("a");b_prop = serializedObject.FindProperty("b");c_prop = serializedObject.FindProperty("c");}//该函数中可以自由编写如何显示序列化数据public override void OnInspectorGUI(){serializedObject.Update();//显示a属性EditorGUILayout.PropertyField(a_prop);//获取a的序列化值bool display = a_prop.boolValue;if (display){//显示b属性EditorGUILayout.PropertyField(b_prop);float weight = b_prop.floatValue;//该条件下,显示c属性if (weight > 0.5f)EditorGUILayout.PropertyField(c_prop);}// 接受序列化赋值serializedObject.ApplyModifiedProperties();}}
实现效果如下所示:
(1)当a的值为false时:
(2)当a的值为true,b的值大于0.5时:
从上图也可以看出,我们可以按需实现自己的Inspector显示方式,而不一定非要用序列化值默认显示方式显示自己的数据。
实现自定义Inpsector思路大致如上代码所示。在获得得序列化数据后,主要工作集中在实现OnInspectorGUI()方法上,所有的显示工作都在这里处理。所以,我们只需要按需实现显示即可。正如上面代码所演示的按条件显示值的案例。
扩展现有Inspector
通过上文,我们知道了如何实现自定义数据在Inspector上的显示。
那么我们该如何定制现有Component在Inspector上的显示呢?
这里以修改Transform属性的Inspector面板为例,说明如何实现对现有Inspector面板的扩展。我们会实现在Transform属性下添加一个Button按钮。
using UnityEngine;using UnityEditor;//这里属性参数设置为Transform[CustomEditor(typeof(Transform))]public class ExtendTransform : Editor {public override void OnInspectorGUI(){Transform t = target as Transform;EditorGUIUtility.LookLikeControls();EditorGUI.indentLevel = 0;//显示PositonEditorGUILayout.BeginHorizontal();Vector3 position = EditorGUILayout.Vector3Field("Position", t.localPosition);EditorGUILayout.EndHorizontal();//显示RotationEditorGUILayout.BeginHorizontal();Vector3 eulerAngles = EditorGUILayout.Vector3Field("Rotation", t.localEulerAngles);EditorGUILayout.EndHorizontal();//显示ScaleEditorGUILayout.BeginHorizontal();Vector3 scale = EditorGUILayout.Vector3Field("Scale", t.localScale);EditorGUILayout.EndHorizontal();//添加ButtonGUILayout.Button("This Is A Additional Button");}}
实现方法如上代码所示。
代码主要是重写OnInspectorGUI()方法,在OnInspectorGUI()中实现对Position、Rotation和Scale的显示。以及添加自定义的Button。
实现效果如下所示。
通过查看UnityEditor的源码也可以看出,Unity自己也是通过这种思路来实现Inspector面板的。
如下所示,Unity实现Transform在Inspector上显示的代码如下所示。从代码中也可以看出,Unity也是通过实现OnInspectorGUI()方法实现Transform在Inspector上显示的。所以,我们可以模仿Unity的实现方法实现自己的Inspector扩展。同时,在遇到对显示的东西不清楚时,也可以通过查看Unity源码得到一些启示。
public override void OnInspectorGUI() { if (TransformInspector.s_Contents == null) { TransformInspector.s_Contents = new TransformInspector.Contents(); } if (!EditorGUIUtility.wideMode) { EditorGUIUtility.wideMode = true; EditorGUIUtility.labelWidth = EditorGUIUtility.currentViewWidth - 212f; } base.serializedObject.Update(); this.Inspector3D(); Transform transform = this.target as Transform; Vector3 position = transform.position; if (Mathf.Abs(position.x) > 100000f || Mathf.Abs(position.y) > 100000f || Mathf.Abs(position.z) > 100000f) { EditorGUILayout.HelpBox(TransformInspector.s_Contents.floatingPointWarning, MessageType.Warning); } base.serializedObject.ApplyModifiedProperties(); } private void Inspector3D() { EditorGUILayout.PropertyField(this.m_Position, TransformInspector.s_Contents.positionContent, new GUILayoutOption[0]); this.m_RotationGUI.RotationField(); EditorGUILayout.PropertyField(this.m_Scale, TransformInspector.s_Contents.scaleContent, new GUILayoutOption[0]); }
总结
以上就是对Unity编辑器扩展开发的入门知识。希望大家通过以上介绍能够了解基本的编辑器扩展开发知识。