拓展Project视图 拓展右键菜单 要将拓展Unity编辑器的脚本放在Editor文件夹下,这个文件夹可以有多个,放在不同目录的子文件夹中,这样开发者就可以按照功能来划分
在editor文件夹中创建ExtendingRightMenu
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 using UnityEngine;using UnityEditor;public class ExtendingRightMenu { [MenuItem("Assets/My Tools/Tools 1" ,false,2) ] static void MyTools1 () { Debug.Log(Selection.activeObject.name); } [MenuItem("Assets/My Tools/Tools 2" , false, 1) ] static void MyTools2 () { Debug.Log(Selection.activeObject.name); } }
拓展创建菜单 在editor文件夹中创建ExtendingCreatMenu
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 using UnityEngine;using UnityEditor;public class ExtendingCreatMenu { [MenuItem("Assets/Create/MyCreate/Sphere" , false, 2) ] static void MyCreateSphere () { GameObject.CreatePrimitive(PrimitiveType.Sphere); } [MenuItem("Assets/Create/MyCreate/Cube" , false, 1) ] static void MyCreateCube () { GameObject.CreatePrimitive(PrimitiveType.Cube); } }
拓展选中按钮布局 用鼠标在Project窗口中选中一个资源后,右侧会出现一个click按钮,点击后在console窗口中打印选中的资源名
在editor文件夹中创建ExtendingLayout
脚本
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 using UnityEngine;using UnityEditor;public class ExtendingLayout { [InitializeOnLoadMethod ] static void InitializeOnLoadMethod () { EditorApplication.projectWindowItemOnGUI = delegate (string guid, Rect selectionRect) { if (Selection.activeObject && guid == AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(Selection.activeObject))) { float width = 50f ; selectionRect.x += selectionRect.width - width; selectionRect.y += 2f ; selectionRect.width = width; GUI.color = Color.red; if (GUI.Button(selectionRect, "click" )) { Debug.LogFormat("click:{0}" , Selection.activeObject.name); } GUI.color = Color.white; } }; } }
监听资源事件 监听Project内资源的创建、删除、移动和保存。重写UnityEditor.AssetModificationProcessor
中的方法
在editor文件夹中创建AssetEventListeners
脚本,并且继承UnityEditor.AssetModificationProcessor
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 System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEditor;public class AssetEventListeners : UnityEditor.AssetModificationProcessor { [InitializeOnLoadMethod ] static void InitializeOnLoadMethod () { EditorApplication.projectWindowChanged = delegate () { Debug.Log("Change" ); }; } public static bool IsOpenForEdit (string assetPath, out string message ) { message = null ; Debug.LogFormat("assetPath:{0}" , assetPath); return true ; } public static void OnWillCreateAsset (string path ) { Debug.LogFormat("path:{0}" , path); } public static string [] OnWillSaveAssets (string [] paths ) { if (paths != null ) { Debug.LogFormat("savePaths{0}" , string .Join("," , paths)); } return paths; } public static AssetMoveResult OnWillMoveAsset (string oldPath, string newPath ) { Debug.LogFormat("from:{0} to {1}" , oldPath, newPath); return AssetMoveResult.DidMove; } public static AssetDeleteResult OnWollDeleteAsset (string assetPath, RemoveAssetOptions option ) { Debug.LogFormat("delete:{0}" , assetPath); return AssetDeleteResult.DidDelete; } }
拓展Hierarchy视图 这个视图对应的是菜单栏GameObject菜单。
拓展GameObject菜单 在editor文件夹中创建ExtendingHierarchyMenu
脚本
然后自定义的菜单就会出现在GameObject菜单里
1 2 3 4 5 6 7 8 9 10 11 12 using UnityEngine;using UnityEditor;public class ExtendingHierarchyMenu { [MenuItem("GameObject/My Create/Cube" ,false,0) ] static void MyCreateCube () { GameObject.CreatePrimitive(PrimitiveType.Cube); } }
拓展选中按钮布局 在editor文件夹中创建ExtendingHierarchyLayout
脚本
点击Hierarchy窗口内的对象,右侧会出现一个图标(这个图标自己导入),点击后输出选中对象的名字
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 using UnityEngine;using UnityEditor;public class ExtendingHierarchyLayout { [InitializeOnLoadMethod ] static void InitializeOnLoadMethod () { EditorApplication.hierarchyWindowItemOnGUI = delegate (int instanceID, Rect selectionRect) { if (Selection.activeObject && instanceID == Selection.activeObject.GetInstanceID()) { float width = 50f ; float height = 20f ; selectionRect.x += (selectionRect.width - width); selectionRect.width = width; selectionRect.height = height; if (GUI.Button(selectionRect, AssetDatabase.LoadAssetAtPath<Texture>("Assets/Unity.png" ))) { Debug.LogFormat("click:{0}" , Selection.activeObject.name); } } }; } }
重写GameObject菜单 通过监听点击事件,打开一个新的菜单窗口,覆盖原有菜单
在editor文件夹中创建OverrideHierarchyMenu
脚本
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 using UnityEngine;using UnityEditor;public class OverrideHierarchyMenu { [MenuItem("Window/Test/ATAO1" ) ] static void Test1 () { } [MenuItem("Window/Test/ATAO2" ) ] static void Test2 () { } [MenuItem("Window/Test/ATAO3" ) ] static void Test3 () { } [InitializeOnLoadMethod ] static void InitializeOnLoadMethod () { EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowGUI; } static void HierarchyWindowGUI (int instanceID, Rect selectionRect ) { if (Event.current != null && selectionRect.Contains(Event.current.mousePosition) && Event.current.button == 1 && Event.current.type <= EventType.MouseUp) { GameObject selectedGameObject = EditorUtility.InstanceIDToObject(instanceID) as GameObject; if (selectedGameObject) { Vector2 mousePosition = Event.current.mousePosition; EditorUtility.DisplayPopupMenu(new Rect(mousePosition.x, mousePosition.y, 0 , 0 ), "Window/Test" , null ); Event.current.Use(); } } } }
我们还可以重写原有的菜单
在editor文件夹中创建OverrideCreateImageMenu
脚本,这样新建的ImageUI组件就不会自动勾选“RaycastTarget”(需要在选中Canvas下新建Image)
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 using UnityEngine;using UnityEditor;using UnityEngine.UI;public class OverrideCreateImageMenu { [MenuItem("GameObject/UI/Image" ) ] static void CreateImage () { if (Selection.activeTransform) { if (Selection.activeTransform.GetComponentInParent<Canvas>()) { Image image = new GameObject("image" ).AddComponent<Image>(); image.raycastTarget = false ; image.transform.SetParent(Selection.activeTransform, false ); Selection.activeTransform = image.transform; } } } }
拓展Inspector视图 拓展原生组件 拓展原生Camera组件
在editor文件夹中创建ExtendingCameraScripts
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 using UnityEngine;using UnityEditor;[CustomEditor(typeof(Camera)) ] public class ExtendingCameraScripts : Editor { public override void OnInspectorGUI () { if (GUILayout.Button("拓展组件" )) { } base .OnInspectorGUI(); } }
拓展继承组件(使用反射) 继承组件是指Unity内部父级会对子级有影响的组件,比如说Transform,我们无法得到他们的绘制方法,先使用反射得到UnityEditor.TransformInspector
,然后调用其内部的OnInspectorGUI()
,会让我们保留系统原有的transform的属性布局,更加美观
在editor文件夹中创建OverrideTransformScripts
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 using UnityEngine;using UnityEditor;using System.Reflection;[CustomEditor(typeof(Transform)) ] public class OverrideTransformScripts : Editor { private Editor m_Editor; private void OnEnable () { m_Editor = Editor.CreateEditor(target, Assembly.GetAssembly(typeof (Editor)).GetType("UnityEditor.TransformInspector" , true )); } public override void OnInspectorGUI () { if (GUILayout.Button("拓展按钮" )) { } m_Editor.OnInspectorGUI(); } }
组件不可编辑 使用GUI.enabled
方法来让组件中部分区域不可编辑
还是使用OverrideTransformScripts
脚本来示例
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 using UnityEngine;using UnityEditor;using System.Reflection;[CustomEditor(typeof(Transform)) ] public class OverriveTransformScripts : Editor { private Editor m_Editor; private void OnEnable () { m_Editor = Editor.CreateEditor(target, Assembly.GetAssembly(typeof (Editor)).GetType("UnityEditor.TransformInspector" , true )); } public override void OnInspectorGUI () { if (GUILayout.Button("拓展按钮上" )) { } GUI.enabled = false ; m_Editor.OnInspectorGUI(); GUI.enabled = true ; if (GUILayout.Button("拓展按钮下" )) { } } }
通过设置hideFlags来让整个对象的内部组件全部不可编辑
我们在hierarchy窗口选择任意游戏对象,然后右键——3D Object——Lock来选择是否全部锁定该对象的Inspector内的组件
在editor文件夹中创建LockInspectorScripts
脚本
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 LockInspectorScripts { [MenuItem("GameObject/3D Object/Lock/Lock" ) ] static void LockInspector () { if (Selection.gameObjects != null ) { foreach (var gameObject in Selection.gameObjects) { gameObject.hideFlags = HideFlags.NotEditable; } } } [MenuItem("GameObject/3D Object/Lock/Unlock" ) ] static void UnlockInspector () { if (Selection.gameObjects != null ) { foreach (var gameObject in Selection.gameObjects) { gameObject.hideFlags = HideFlags.None; } } } }
HideFlags可用属性列表
HideFlags.None: 清除状态
HideFlags.DontSave: 设置对象不会被保存(仅编辑模式下使用,运行时剔除掉)
HideFlags.DontSaveInBuild: 设置对象Build后不会被保存
HideFlags.DontSaveInEditor: 设置对象编辑模式下不会被保存
HideFlags.DontUnloadUnusedAsset: 设置对象不会被Resources.UnloadUnusedAssets()
卸载无用资源时卸载掉。
HideFlags.HideAndDontSave: 设置对象隐藏,并且不会被保存
HideFlags.HideInHierarchy: 设置对象在HIerarchy视图中隐藏
HideFlags.HideInInspector: 设置对象在Inspector视图中隐藏
HideFlags.NotEditable: 设置对象不可被编辑
组件Context菜单 组件Context菜单是指组件右上角或者右键点击后弹出的菜单
在editor文件夹中创建ExtendingContextMenu
脚本
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 ExtendingContextMenu { [MenuItem("CONTEXT/Transform/New Context 1" ) ] public static void NewContext1 (MenuCommand command ) { Debug.Log(command.context.name); } [MenuItem("CONTEXT/Transform/New Context 2" ) ] public static void NewContext2 (MenuCommand command ) { Debug.Log(command.context.name); } }
在Script文件夹中创建ExtendingOwnScriptContext
脚本,我们为自己的脚本component自定义Context,注意这是组件,这个脚本要放在Script文件夹里并继承MonoBehaviour
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using UnityEngine;#if UNITY_EDITOR using UnityEditor;#endif public class ExtendingOwnScriptContext : MonoBehaviour { public string contextName; #if UNITY_EDITOR [MenuItem("CONTEXT/ExtendingOwnScriptContext/New Context1" ) ] public static void NewContext2 (MenuCommand command ) { ExtendingOwnScriptContext scriptContext = command.context as ExtendingOwnScriptContext; scriptContext.contextName = "捏麻麻地" ; } #endif }
除了MenuItem,创建自定义Context还有一个更简易的特性写法ContextMenu
这里演示一个覆盖系统的Remove Component的脚本
1 2 3 4 5 6 7 8 [ContextMenu("Remove Component" ) ] void RemoveComponent () { UnityEditor.EditorApplication.delayCall = delegate () { DestroyImmediate(this ); }; }
拓展Scene视图 添加辅助元素 使用OnDrawGizmosSelected
方法来添加辅助元素,各种添加辅助元素的方式都在Gizmos
工具类里面。
绘制辅助元素需要挂载脚本,所以需要继承MonoBehaviour
在Script文件夹中创建GizmosForSelected
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using UnityEngine;public class GizmosForSelected : MonoBehaviour { private void OnDrawGizmosSelected () { Gizmos.color = Color.red; Gizmos.DrawLine(gameObject.transform.position, Vector3.one); Gizmos.DrawCube(Vector3.one, Vector3.one); } private void OnDrawGizmos () { Gizmos.DrawSphere(transform.position, 1f ); } }
辅助UI 可以在Scene视图中添加EditorGUI,就像拓展Inspector视图原生组件一样,重写OnSceneGUI
方法来绘制
使用下面的脚本,在Scene视图中选中摄像机,会显示摄像机的坐标,并且左上角会出现一个按钮和一个Label,点击后显示摄像机名称
在Editor文件夹中创建ExtendingSceneWindow
脚本
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 using UnityEngine;using UnityEditor;[CustomEditor(typeof(Camera)) ] public class ExtendingSceneWindow : Editor { private void OnSceneGUI () { Camera camera = target as Camera; if (camera != null ) { Handles.color = Color.red; Handles.Label(camera.transform.position, camera.transform.position.ToString()); Handles.BeginGUI(); GUI.backgroundColor = Color.red; if (GUILayout.Button("Click" , GUILayout.Width(200f ))) { Debug.LogFormat("Click{0}" , camera.name); } GUILayout.Label("Label" ); Handles.EndGUI(); } } }
常驻辅助UI 订阅SceneView.duringSceneGui
事件,就可以实现常驻了
在Editor文件夹中创建RemainingSceneWindow
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 using UnityEngine;using UnityEditor;public class RemainingSceneWindow { [InitializeOnLoadMethod ] static void InitializeOnLoadMethod () { SceneView.duringSceneGui += delegate (SceneView sceneView) { Handles.BeginGUI(); GUI.Label(new Rect(0 , 0 , 50f , 15f ), "标题" ); GUI.Button(new Rect(0 , 20f , 50f , 50f ), AssetDatabase.LoadAssetAtPath<Texture>("Assets/Unity.png" )); Handles.EndGUI(); }; } }
禁用选中Scene内的对象 代码只做演示,新版本的Unity中,这个功能只需要点击HIerarchy窗口中对应物体左侧的小指头 即可,这样的话Scene窗口中的物体就不会被选中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 using UnityEngine;using UnityEditor;public class UnselectableObjectOnScene { [InitializeOnLoadMethod ] static void InitializeOnLoadMethod () { SceneView.duringSceneGui += delegate (SceneView sceneView) { Event e = Event.current; if (e != null ) { int controlID = GUIUtility.GetControlID(FocusType.passive); if (e.type == EventType.Layout) { HandleUtility.AddDefaultControl(controlID); } } }; } }
父节点锁定 为了防止在Scene视图中选择到子节点对象,对父节点使用SelectionBase
特性脚本
代码演示如下
1 2 [SelectionBase ] public class RootScript : MonoBehaviour {}
拓展Game视图 在Game视图中拓展,最经典的就是使用OnGUI
方法了,但是直接使用此方法需要挂载脚本,并且要运行才可以
使用ExecuteInEditMode
特性就可以在编辑模式下运行,不过仍然需要挂载脚本
一般情况下此特性需要配合Unity_Editor预编译指令,发布后剥离掉只有在编辑器中应用的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using UnityEngine;#if UNITY_EDITOR [ExecuteInEditMode ] public class ExtendingGameWindow : MonoBehaviour { private void OnGUI () { if (GUILayout.Button("Click" )) { Debug.Log("Click!!!" ); } GUILayout.Label("Hello World!" ); } } #endif