Unity 扩展编辑器
扩展编辑器: 可以在编辑器的任何地方加上自己自定义的按钮
一、扩展编辑器概述
写的类引入 UnityEditor 命名空间
写的方法必须是static的 这样编辑器就可以通过类名调用 而不需要创建实例
在方法的顶部标记为[MenuItem()]
MenuItem() 是 UnityEditor 的 静态方法 有许多重载函数 可以看下面的源码 对应参数的解释也已经很详细了
// Decompiled with JetBrains decompiler // Type: UnityEditor.MenuItem // Assembly: UnityEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null // MVID: 9C976F45-3B3D-47EA-8C1B-74624247F64D // Assembly location: D:\Unity\2018.4.32f1\Editor\Data\Managed\UnityEditor.dll using System; using System.Collections.Generic; using System.Linq; using UnityEngine.Scripting; namespace UnityEditor { /// <summary> /// <para>The MenuItem attribute allows you to add menu items to the main menu and inspector context menus.</para> /// </summary> [RequiredByNativeCode] [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public sealed class MenuItem : Attribute { private static readonly string[] kMenuItemSeparators = new string[1] { "/" }; public string menuItem; public bool validate; public int priority; /// <summary> /// <para>Creates a menu item and invokes the static function following it, when the menu item is selected.</para> /// </summary> /// <param name="itemName">The itemName is the menu item represented like a pathname. /// For example the menu item could be "GameObject/Do Something".</param> /// <param name="isValidateFunction">If isValidateFunction is true, this is a validation /// function and will be called before invoking the menu function with the same itemName.</param> /// <param name="priority">The order by which the menu items are displayed.</param> public MenuItem(string itemName) : this(itemName, false) { } /// <summary> /// <para>Creates a menu item and invokes the static function following it, when the menu item is selected.</para> /// </summary> /// <param name="itemName">The itemName is the menu item represented like a pathname. /// For example the menu item could be "GameObject/Do Something".</param> /// <param name="isValidateFunction">If isValidateFunction is true, this is a validation /// function and will be called before invoking the menu function with the same itemName.</param> /// <param name="priority">The order by which the menu items are displayed.</param> public MenuItem(string itemName, bool isValidateFunction) : this(itemName, isValidateFunction, !itemName.StartsWith("GameObject/Create Other") ? 1000 : 10) { } /// <summary> /// <para>Creates a menu item and invokes the static function following it, when the menu item is selected.</para> /// </summary> /// <param name="itemName">The itemName is the menu item represented like a pathname. /// For example the menu item could be "GameObject/Do Something".</param> /// <param name="isValidateFunction">If isValidateFunction is true, this is a validation /// function and will be called before invoking the menu function with the same itemName.</param> /// <param name="priority">The order by which the menu items are displayed.</param> public MenuItem(string itemName, bool isValidateFunction, int priority) : this(itemName, isValidateFunction, priority, false) { } internal MenuItem(string itemName, bool isValidateFunction, int priority, bool internalMenu) { itemName = MenuItem.NormalizeMenuItemName(itemName); this.menuItem = !internalMenu ? itemName : "internal:" + itemName; this.validate = isValidateFunction; this.priority = priority; } private static string NormalizeMenuItemName(string rawName) { return string.Join(MenuItem.kMenuItemSeparators[0], ((IEnumerable<string>) rawName.Split(MenuItem.kMenuItemSeparators, StringSplitOptions.None)).Select<string, string>((Func<string, string>) (token => token.Trim())).ToArray<string>()); } } }
二、扩展编辑器分类
想要对路径进行分类 如图 多一个分割线

调用MenuItem() 的时候 相邻的两个函数 设置 priority的参数 相差11即可
如上图 假设 Camera 为 10 那么 CenterOnChildren 设置为大于21 就会在它们俩中间多一条分割线
三、在Hierarchy下 鼠标右键添加按钮
想要在Hierarchy下 鼠标右键添加按钮 可以将路径设置在GameObject下 priority设置在25以内 (0-24) 如图


using UnityEngine; using UnityEditor; public class Tools { [MenuItem("Tools/Test1", false, 0)] static void Test1() { } [MenuItem("Tools/Test2", false, 1)] static void Test2() { } [MenuItem("Tools/Test3", false, 2)] static void Test3() { } [MenuItem("GameObject/MyCustom", false, 24)] // 注意这里 如果是25 就会在新的分类 static void MyCustom() { } }
四、在Project下 鼠标右键添加按钮
想要在Project下 鼠标右键添加按钮 可以将路径设置在Assets下 如图


using UnityEngine; using UnityEditor; public class Tools { [MenuItem("Assets/MyCustom", false, 24)] static void MyCustom() { } }
五、在Inspector的组件上添加右键按钮

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; public class PlayerEditor { [MenuItem("CONTEXT/PlayerHealth/InitHealthAndSpeed")]// CONTEXT 组件名 按钮名 static void InitHealthAndSpeed( MenuCommand cmd )//menucommand是当前正在操作的组件 { //Debug.Log(cmd.context.GetType().FullName); CompleteProject.PlayerHealth health = cmd.context as CompleteProject.PlayerHealth; health.startingHealth = 200; health.flashSpeed = 10; Debug.Log("Init"); } [MenuItem("CONTEXT/Rigidbody/Clear")] static void ClearMassAndGravity( MenuCommand cmd ) { Rigidbody rgd = cmd.context as Rigidbody; rgd.mass = 0; rgd.useGravity = false; } }
六、给扩展属性增加可用性校验
using UnityEngine; using UnityEditor; public class Tools { // 第二个参数设置为 true 表示下面这个方法是一个验证方法 [MenuItem("GameObject/MyDelete", true, 11)] // 注意 : 这个验证的函数名可以随意 但是 MenuItem方法下的路径 一定要跟 按键是同一个"GameObject/MyDelete" // 这里的返回值是一个bool型 如果return true 则按钮可以用 否则按钮是置灰 static bool MyDeleteValidate() { if (Selection.objects.Length > 0) { return true; } else { return false; } } [MenuItem("GameObject/MyDelete", false, 11)] static void MyDelete() { foreach (Object o in Selection.objects) { // 使用Undo 是可以撤销的 Undo.DestroyObjectImmediate(o); } } }
七、给扩展的组件按钮增加快捷键
//%=ctrl #=shift &=alt [MenuItem("Tools/test2 %q",false,100)] static void Test2() { Debug.Log("Test2"); } [MenuItem("Tools/test3 %t",false,0)] static void Test3() { Debug.Log("Test3"); }
八、撤销 Undo.DestroyObjectImmediate(o);//利用Undo进行的删除操作 是可以撤销的
九、在组件的public 属性上增加扩展

十、创建对话框
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; public class EnemyChange : ScriptableWizard { [MenuItem("Tools/CreateWizard")] static void CreateWizard() { ScriptableWizard.DisplayWizard<EnemyChange>("统一修改敌人","Change And Close","Change"); } public int changeStartHealthValue = 10; public int changeSinkSpeedValue = 1; const string changeStartHealthValueKey = "EnemyChange.changeStartHealthValue"; const string changeSinkSpeedValueKey = "EnemyChange.changeSinkSpeedValue"; //当窗口被创建出来的时候调用的 void OnEnable() { changeStartHealthValue = EditorPrefs.GetInt(changeStartHealthValueKey, changeStartHealthValue); changeSinkSpeedValue = EditorPrefs.GetInt(changeSinkSpeedValueKey, changeSinkSpeedValue); } //检测create按钮的点击 void OnWizardCreate() { GameObject[] enemyPrefabs = Selection.gameObjects; EditorUtility.DisplayProgressBar("进度", "0/" + enemyPrefabs.Length + " 完成修改值", 0); int count = 0; foreach (GameObject go in enemyPrefabs) { CompleteProject.EnemyHealth hp = go.GetComponent<CompleteProject.EnemyHealth>(); Undo.RecordObject(hp, "change health and speed"); hp.startingHealth += changeStartHealthValue; hp.sinkSpeed += changeSinkSpeedValue; count++; EditorUtility.DisplayProgressBar("进度", count+"/" + enemyPrefabs.Length + " 完成修改值", (float)count/enemyPrefabs.Length); } EditorUtility.ClearProgressBar(); ShowNotification(new GUIContent(Selection.gameObjects.Length + "个游戏物体的值被修改了")); } void OnWizardOtherButton() { OnWizardCreate(); } //当前字段值修改的时候会被调用 void OnWizardUpdate() { errorString = null; helpString = null; if (Selection.gameObjects.Length > 0) { helpString = "您当前选择了" + Selection.gameObjects.Length + "个敌人"; } else { errorString = "请选择至少一个敌人"; } EditorPrefs.SetInt(changeStartHealthValueKey, changeStartHealthValue); EditorPrefs.SetInt(changeSinkSpeedValueKey, changeSinkSpeedValue); } void OnSelectionChange() { OnWizardUpdate(); } }
十一、创建自定义窗口
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; public class MyWindow : EditorWindow { [MenuItem("Window/show mywindow")] static void ShowMyWindow() { MyWindow window= EditorWindow.GetWindow<MyWindow>(); window.Show(); } private string name=""; void OnGUI() { GUILayout.Label("这是我的窗口"); name = GUILayout.TextField(name); if (GUILayout.Button("创建")) { GameObject go = new GameObject(name); Undo.RegisterCreatedObjectUndo(go, "create gameobject"); } } }
浙公网安备 33010602011771号