MenuItem特性可以拓展Unity原有的菜单和脚本的Context菜单,当然也可以自定义自己的菜单
所有MenuItem执行的方法都必须是静态的
覆盖系统菜单 1 2 3 4 5 6 [MenuItem("GameObject/UI/Text" ) ] static void MyTextMethod (){ Debug.Log("Text" ); }
自定义菜单 在Editor文件夹中新建CustomTitleMenu
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 using UnityEngine;using UnityEditor;public class CustomTitleMenu { [MenuItem("Root/Test1" , false, 0) ] static void MyTest1 () { } [MenuItem("Root/Test2" , false, 1) ] static void MyTest2 () { } [MenuItem("Root/Test/My Test" ) ] static void MyTest () { } [MenuItem("Root/Test/My Test" , true, 20) ] static bool MyTestValidation () { return false ; } [MenuItem("Root/Test3" , false, 3) ] static void MyTest3 () { var menuPath = "Root/Test3" ; bool mchecked = Menu.GetChecked(menuPath); Menu.SetChecked("Root/Test3" , !mchecked); } }
结果如下图所示
原生自定义菜单 使用下列脚本,在Scene窗口中单击右键就可以弹出一个菜单
在Editor文件夹中新建CreatingOriginMenu
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 using UnityEngine;using UnityEditor;public class CreatingOriginMenu { [InitializeOnLoadMethod ] static void InitializeOnLoadMethod () { SceneView.duringSceneGui += delegate (SceneView sceneView) { Event e = Event.current; if (e != null && e.button == 1 && e.type == EventType.MouseUp) { Vector2 mousePosition = e.mousePosition; var options = new GUIContent[] { new GUIContent("Text1" ), new GUIContent("Text2" ), new GUIContent("" ), new GUIContent("Text3" ), new GUIContent("Text4" ), }; var selected = -1 ; var userData = Selection.activeGameObject; var width = 100 ; var height = 100 ; var position = new Rect(mousePosition.x, mousePosition.y - height, width, height); EditorUtility.DisplayCustomMenu(position, options, selected, delegate (object data, string [] opt, int select ) { Debug.Log(opt[select ]); }, userData); e.Use(); } }; } }
拓展全局自定义按键 在Editor文件夹中新建CustomHotKey
脚本
1 2 3 4 5 6 7 8 9 10 11 12 using UnityEngine;using UnityEditor;public class CustomHotKey { [MenuItem("Assets/Hotkey %#D" ,false,-1) ] static void MyHotKey () { Debug.Log("Ctrl Shift D" ); } }
符号表示的是快捷键,
%: Windows的Ctrl键、macOS下的Commad键
#: Shift键
&: Alt键
LEFT/RIGHT/UP/DOWN: 表示上下左右键
F1……F2:表示F1~F12快捷键
HOME、END、PGUP、PGUN
面板拓展 使用EditorGUI来自定义脚本Inspector的序列化方式以及拓展自己的窗口等
Inspector面板 拓展方式类似于之前拓展Inspector窗口时使用的方式,使用CustomEditor
特性
在Script文件夹中新建InspectorWithEditorGUI
脚本
在实际应用中,建议将此脚本后面编辑器的部分拆分出来放在Editor文件夹里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 using UnityEngine;#if UNITY_EDITOR using UnityEditor;#endif public class InspectorWithEditorGUI : MonoBehaviour { public Vector3 scollPos; public int myId; public string myName; public GameObject prefab; public MyEnum myEnum = MyEnum.One; public bool toggle1; public bool toggle2; public enum MyEnum { One = 1 , Two, } } #if UNITY_EDITOR [CustomEditor(typeof(InspectorWithEditorGUI)) ] public class InspectorWithEditor : Editor { private bool m_EnableToggle; public override void OnInspectorGUI () { InspectorWithEditorGUI script = target as InspectorWithEditorGUI; script.scollPos = EditorGUILayout.BeginScrollView(script.scollPos, false , true ); script.myName = EditorGUILayout.TextField("text" , script.myName); script.myId = EditorGUILayout.IntField("int" , script.myId); script.prefab = EditorGUILayout.ObjectField("GameObject" , script.prefab, typeof (GameObject), true ) as GameObject; EditorGUILayout.BeginHorizontal(); GUILayout.Button("1" ); GUILayout.Button("2" ); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); script.myEnum = (InspectorWithEditorGUI.MyEnum)EditorGUILayout.EnumPopup("My Enum:" , script.myEnum); EditorGUILayout.EndHorizontal(); m_EnableToggle = EditorGUILayout.BeginToggleGroup("EnableToggle" , m_EnableToggle); script.toggle1 = EditorGUILayout.Toggle("Toggle1" , script.toggle1); script.toggle2 = EditorGUILayout.Toggle("Toggle2" , script.toggle2); EditorGUILayout.EndToggleGroup(); EditorGUILayout.EndScrollView(); } } #endif
EditorWindows窗口 使用EditorWindows
可以制作自己的窗口,继承后使用EditorWindow.GetWindow
方法即可打开自己的窗口
在Editor文件夹中新建CustomEditorWindow
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 using UnityEngine;using UnityEditor;public class CustomEditorWindow : EditorWindow { [MenuItem("Window/My Window" ) ] static void Init () { CustomEditorWindow window = (CustomEditorWindow)EditorWindow.GetWindow(typeof (CustomEditorWindow),false ,"MY CUSTOM" ); window.Show(); } private Texture m_Mytexture = null ; private float m_Myfloat = 0.5f ; private void Awake () { Debug.LogFormat("窗口初始化时调用" ); m_Mytexture = AssetDatabase.LoadAssetAtPath<Texture>("Assets/unity.png" ); } private void OnGUI () { GUILayout.Label("Hello World!" , EditorStyles.boldLabel); m_Myfloat = EditorGUILayout.Slider("滑块" , m_Myfloat, -5 , 5 ); GUI.DrawTexture(new Rect(0 , 30 , 100 , 100 ), m_Mytexture); } private void OnDestroy () { Debug.LogFormat("窗口销毁时调用" ); } private void OnFocus () { Debug.LogFormat("窗口有焦点时调用" ); } private void OnHierarchyChange () { Debug.LogFormat("hierarchy窗口发生变化调用" ); } private void OnInspectorUpdate () { } private void OnLostFocus () { Debug.LogFormat("失去焦点时调用" ); } private void OnProjectChange () { Debug.LogFormat("Project视图发生变化时调用" ); } private void OnSelectionChange () { Debug.LogFormat("在Project或Hierarchy窗口选择一个对象时调用" ); } private void Update () { } }
EditorWindow下拉菜单 为了使自定义的Editor窗口也拥有自定义的下拉菜单,需要先实现IHasCustomMenu接口
在Editor文件夹中新建EditorWindowWithDropdown
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 using UnityEngine;using UnityEditor;public class EditorWindowWithDropdown : EditorWindow ,IHasCustomMenu { void IHasCustomMenu.AddItemsToMenu(GenericMenu menu) { menu.AddDisabledItem(new GUIContent("Disable" )); menu.AddItem(new GUIContent("Test1" ), true , () => { Debug.Log("Test1" ); }); menu.AddItem(new GUIContent("Test2" ), true , () => { Debug.Log("Test2" ); }); menu.AddSeparator("Test/" ); menu.AddItem(new GUIContent("Test/Test3" ), true , () => { Debug.Log("Test3" ); }); } [MenuItem("Window/Open My Window" ) ] static void Init () { EditorWindowWithDropdown windowWithDropdown = (EditorWindowWithDropdown)EditorWindow.GetWindow(typeof (EditorWindowWithDropdown),false ,"WithDropDown" ); windowWithDropdown.Show(); } }
通过AddItem
来添加列表元素,并且监听选择后的事件
预览窗口 有些资源选择后,Inspector面板下方会出现预览窗口,但是有些资源是没有预览信息的,我们可以重写覆盖此资源的预览信息
在Editor文件夹中新建CustomPreviewWindow
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using UnityEngine;using UnityEditor;[CustomPreview(typeof(GameObject)) ] public class CustomPreviewWindow : ObjectPreview { public override bool HasPreviewGUI () { return true ; } public override void OnPreviewGUI (Rect r, GUIStyle background ) { GUI.DrawTexture(r, AssetDatabase.LoadAssetAtPath<Texture>("Assets/unity.png" )); GUILayout.Label("Hello World" ); } }
继承ObjectPreview
并且重写OnPreviewGUI
方法,在CustomPreview(typeof(GameObject))
中的GameObject按自己的需要修改为系统对象或自定义脚本对象
获取预览信息 有一些资源是有预览信息的,如果需要在自定义窗口中也能显示资源的预览信息,可以使用下面的脚本
在Editor文件夹中新建GetPreviewMessage
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 using UnityEngine;using UnityEditor;public class GetPreviewMessage : EditorWindow { private GameObject m_MyGo; private Editor m_MyEditor; [MenuItem("Window/Open My Preview" ) ] static void Init () { GetPreviewMessage window = (GetPreviewMessage)EditorWindow.GetWindow(typeof (GetPreviewMessage), false , "Preview" ); window.Show(); } private void OnGUI () { m_MyGo = (GameObject)EditorGUILayout.ObjectField(m_MyGo, typeof (GameObject), true ); if (m_MyGo != null ) { if (m_MyEditor == null ) { m_MyEditor = Editor.CreateEditor(m_MyGo); } m_MyEditor.OnPreviewGUI(GUILayoutUtility.GetRect(500 , 500 ), EditorStyles.whiteLabel); } } }
打开自定义的窗口,然后拖拽进一个有预览信息的物体
Unity编辑器的源码 通过Unity Hub安装的Unity,默认安装目录下的编辑器源码地址:“C:\Program Files\Unity\Hub\Editor\2020.3.6f1c1\Editor\Data\Managed\UnityEditor.dll”可参考
查看DLL 常用的查看工具:.NET Reflectior以及ILSpy,可以使用Visual Studio查看类名和方法,但是无法查看源码
清空控制台日志 在Editor文件夹中新建ClearConsoleWindow
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 using UnityEngine;using UnityEditor;using System.Reflection;public class ClearConsoleWindow { [MenuItem("Root/CreatConsole" ) ] static void CreatConsole () { Debug.Log("CreatConsole" ); } [MenuItem("Root/CleanConsole" ) ] static void CleanConsole () { Assembly assembly = Assembly.GetAssembly(typeof (Editor)); MethodInfo methodInfo = assembly.GetType("UnityEditor.LogEntries" ).GetMethod("Clear" ); methodInfo.Invoke(new object (), null ); } }
获取EditorStyles样式 样式是开发C#应用交互时使用的各种风格,Unity并没有明确说明编辑器使用的样式,我们反射出EditorStyles
的属性(Properties),找到GUIStyle并最终在OnGUI中预览出来
在Editor文件夹中新建GetEditorStyles
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 using System.Collections.Generic;using UnityEngine;using UnityEditor;using System.Reflection;public class GetEditorStyles : EditorWindow { static List<GUIStyle> styles = null ; [MenuItem("Window/Open Edtior Styles" ) ] public static void Test () { EditorWindow.GetWindow<GetEditorStyles>("styles" ); styles = new List<GUIStyle>(); foreach (PropertyInfo fi in typeof (EditorStyles ).GetProperties (BindingFlags.Static|BindingFlags.Public|BindingFlags.NonPublic )) { object o = fi.GetValue(null , null ); if (o.GetType() == typeof (GUIStyle)) { styles.Add(o as GUIStyle); } } } public Vector2 scrollPosition = Vector2.zero; private void OnGUI () { scrollPosition = GUILayout.BeginScrollView(scrollPosition); for (int i = 0 ; i < styles.Count; i++) { GUILayout.Label("EditorStyles." + styles[i].name, styles[i]); } GUILayout.EndScrollView(); } }
根据反射出来的样式名称,制作Unity编辑器时需要的样式,直接设置style名字即可。(比如之前获取预览信息使用的EditorStyles.WhiteLabel
)
获取内置图标样式 Unity内置了很多图标样式,接下来使用一个小脚本来列出所有的图标
在Editor文件夹中新建GetAllIcons
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 using System.Collections.Generic;using UnityEngine;using UnityEditor;public class GetAllIcons : EditorWindow { [MenuItem("Window/Open Icons" ) ] public static void OnenIconWindow () { EditorWindow.GetWindow<GetAllIcons>("Icons" ); } private Vector2 m_Scroll; private List<string > m_Icons = null ; private void Awake () { m_Icons = new List<string >(); Texture2D[] t = Resources.FindObjectsOfTypeAll<Texture2D>(); foreach (Texture2D x in t) { Debug.unityLogger.logEnabled = false ; GUIContent gc = EditorGUIUtility.IconContent(x.name); Debug.unityLogger.logEnabled = true ; if (gc != null && gc.image != null ) { m_Icons.Add(x.name); } } Debug.Log(m_Icons.Count); } private void OnGUI () { m_Scroll = GUILayout.BeginScrollView(m_Scroll); float width = 50f ; int count = (int )(position.width / width); for (int i = 0 ; i < m_Icons.Count; i += count) { GUILayout.BeginHorizontal(); for (int j = 0 ; j < count; j++) { int index = i + j; if (index < m_Icons.Count) GUILayout.Button(EditorGUIUtility.IconContent(m_Icons[index]), GUILayout.Width(width), GUILayout.Height(30 )); } GUILayout.EndHorizontal(); } EditorGUILayout.EndScrollView(); } }
拓展默认面板(文件夹面板) 在Unity中点击文件夹,inspector中没有提供多余的显示信息,我们来拓展一下这个默认面板
在Editor文件夹中新建ExtendingFolderPanel
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using UnityEngine;using UnityEditor;[CustomEditor(typeof(UnityEditor.DefaultAsset)) ] public class ExtendingFolderPanel : Editor { public override void OnInspectorGUI () { string path = AssetDatabase.GetAssetPath(target); GUI.enabled = true ; if (path.EndsWith(string .Empty)) { GUILayout.Label("拓展文件夹" ); GUILayout.Button("我是文件夹" ); } } }