Unity拓展编辑器


Edtior文件位置灵活,可以作为多个目录的子文件夹存在
按功能划分不同的Editor目录

Project拓展

public class ProjectEditor
	{
   	    //1.Project下的右键窗口菜单项,与具体文件无关,右键就行
        [MenuItem("Assets/My Right Tools/PrintName",false,2)] //2排序位置,越小越往上
        static void MyRightTools()
        {
            Debug.Log(Selection.activeObject.name);
        }

        //2.Project右键窗口创建基元对象,创建在Hierarchy中,Project里是不能有对象的,只有有预制体。
        [MenuItem("Assets/My Right Tools/CreateCube",false,1)]
        static void CreateCube()
        {
            GameObject.CreatePrimitive(PrimitiveType.Cube);
        }

        //3. 选中的资源拓展
        [InitializeOnLoadMethod] //c#每次编译完成调用一次
        static void InitializeOnLoadMethod()
        {
            EditorApplication.projectWindowItemOnGUI = delegate(string guid,Rect selectionRect) //利用委托用ongui的事件来重新绘制project里面的item
            {
                if(Selection.activeObject&&guid==AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(Selection.activeObject))) //在选中物体的时候
                {
                    float width = 50f;
                    selectionRect.x += (selectionRect.width - width); //确保最右端一样
                    selectionRect.y += 2f; //起点增加是往上走,y,不射h默认占满selection.activeobject。
                    selectionRect.width = width;
                    GUI.color = Color.red;
                    //开始图形的绘制
                    if (GUI.Button(selectionRect, "click"))
                    {
                        Debug.LogFormat("click:{0}", Selection.activeObject.name); //格式化debug
                    }
                    //颜色回溯
                    GUI.color = Color.white;
                }
            }; //匿名委托
        }
	}

Hierarchy拓展

    public class HierarchyEditor
    {
        //1.Hierarchy面板下Create菜单增加使用GameObject/路径
        [MenuItem("GameObject/My Create/Create Cube",false,1)]
        static void MyCreateCube()
        {
            GameObject.CreatePrimitive(PrimitiveType.Cube);
        }
      [MenuItem("GameObject/My Create/Create Cube2", false, 12)] //每10个空位是一条分割线,所以n+11就会有一条分隔符了
        static void MyCreateCube2()
        {
            GameObject.CreatePrimitive(PrimitiveType.Cube);
        }

        //2. 选中的资源拓展
        [InitializeOnLoadMethod] //c#每次编译完成调用一次
        static void InitializeOnLoadMethod()
        {
            EditorApplication.hierarchyWindowItemOnGUI = delegate (int instanceId, Rect selectionRect) //利用委托用ongui的事件来重新绘制project里面的item
            {
                if (Selection.activeObject && instanceId == Selection.activeObject.GetInstanceID()) //在选中物体的时候
                {
                    float width = 50f;
                    float height = 20f;
                    selectionRect.x += (selectionRect.width - width); //右对齐
                    selectionRect.width = width;
                    selectionRect.height = height;//下无法对齐,因为hierarchy下的对象大小都一样,所以这里用固定高度
                    GUI.color = Color.red;
                    //开始图形的绘制
                    if (GUI.Button(selectionRect,"click"))
                    {
                        Debug.LogFormat("click:{0}", Selection.activeObject.name); //格式化debug
                    }
                    //颜色回溯
                    GUI.color = Color.white;
                }
            }; //匿名委托
        }

        [MenuItem("My PopUp/Test1")]
        static void Test1()
        {
            Debug.LogFormat("My:{0}", "Test1");
        }
        [MenuItem("My PopUp/Test2")]
        static void Test2()
        {
            Debug.Log("Test2");
        }

        //3.右键的重写菜单
        [InitializeOnLoadMethod]
        static void StartinitializeOnLoadMethod()
        {
            EditorApplication.hierarchyWindowItemOnGUI += delegate (int instanceID, Rect selectionRect) //这句话保证右键一定是要点击在对象上才会触发,空白右键无对象物体不触发
            {
                //右键点击且拾起,
                if (Event.current != null && selectionRect.Contains(Event.current.mousePosition) && Event.current.button == 1 && Event.current.type <= EventType.MouseUp)
                {
                    GameObject activeObject = EditorUtility.InstanceIDToObject(instanceID) as GameObject; //根据id获得游戏对象
                    if (activeObject)
                    {
                        EditorUtility.DisplayPopupMenu(new Rect(Event.current.mousePosition.x, Event.current.mousePosition.y, 0, 0), "My PopUp", null); //核心,绘制新的菜单
                        Event.current.Use();//弃用原来的方法,使用新的菜单
                    }
                }
            };
        }

        //4.重写自带右键菜单的行为
        //自动勾选RaycastTarget==false
        [MenuItem("GameObject/UI/Image")]
        static void CreateImage()
        {
            if(Selection.activeTransform)
            {
                if(Selection.activeTransform.GetComponentInParent<Canvas>()) //一定要在canvas下
                {
                    Image image = new GameObject("Image").AddComponent<Image>();
                    image.raycastTarget = false; //核心
                    image.transform.SetParent(Selection.activeTransform, false); //和父亲坐标一起
                    //设置选中状态
                    Selection.activeTransform = image.transform;
                }
            }
        }
    }

Inspector拓展

    //1.拓展原生组件,只能加在上方或者下方
    [CustomEditor(typeof(Camera))] //只能针对于类使用,相当于一个组件一个类了
    public class CameraEditor:Editor //保证Override, OnInspectorGUI
    {
        public override void OnInspectorGUI()
        {
            if(GUILayout.Button("拓展按钮"))
            { }
            base.OnInspectorGUI();
        }
    }

    //2.拓展继承组件,系统组件已经在unity内置中重写了绘制方法,且该方法已为私有,外界无法访问原生的,只能访问重写之后的
    //解决:反射访问私有的真正原生的绘制方法
    [CustomEditor(typeof(Transform))]
    public class TransformEditor:Editor
    {
        private Editor _Editor;
        private void OnEnable() //只有这个组件脚本有用就会被执行
        {
            //一个dll包含多个命名空间
            _Editor = Editor.CreateEditor(target/*正在被检视的物体*/, Assembly.GetAssembly(typeof(Editor)/*Editor dll下有一个UnityEditor命名空间的transformInspector类型的类*/).GetType("UnityEditor.TransformInspector",true/*出错了反射会抛出错误*/));
        }
        public override void OnInspectorGUI()
        {
            if(GUILayout.Button("拓展按钮"))
            {
            }
            //base.OnInspectorGUI(); 重写后的不好看
            _Editor.OnInspectorGUI();
        }
    }

    //3.通过highFlag可以让某个对象或者某个组件在属性面板上不可编辑
    public class NotEditor
    {
        [MenuItem("GameObject/3D Object/Lock/LockTransform",false,0)]
        static void Lock()
        {
            if(Selection.gameObjects!=null) //选择了物体就要把一群物体对应的组件禁用
            {
                foreach (var item in Selection.gameObjects)
                {
                    item.transform.hideFlags = HideFlags.NotEditable;
                }
            }
        }
        [MenuItem("GameObject/3D Object/Lock/UnLockTransform", false, 0)]
        static void UnLock()
        {
            if (Selection.gameObjects != null) //选择了物体就要把一群物体对应的组件禁用
            {
                foreach (var item in Selection.gameObjects)
                {
                    item.transform.hideFlags = HideFlags.None; //取消标记
                }
            }
        }
    }

    //4.Context菜单,属性面板上右键组件的菜单修改
    public class ContextEditor:MonoBehaviour //使用Unity组件不要忘了MonoBehavior
    {
        //新增
        [MenuItem("CONTEXT/Transform/New Context1")]
        public static void NewContext1(MenuCommand command) //右键组件的组件上下文
        {
            Debug.Log(command.context.name);
        }
        [MenuItem("CONTEXT/Transform/New Context2")] //默认新生成的priorId减少,减少在下面
        public static void NewContext2(MenuCommand command) //右键组件的组件上下文
        {
            Debug.Log(command.context.name);
        }
        //如果组件是脚本的话还可以把command.context强制转换成对应的脚本类型,然后修改公共成员
        //略
        //如果名字和系统的菜单项名称一样,则会覆盖原来的内容
        [ContextMenu("Remove Component")]
        void RemoveCompoent()
        {
            Debug.Log("RemoveComponent");
            EditorApplication.delayCall = delegate ()
            {
                DestroyImmediate(this);
            };
        }
    }

Scene拓展

#if UNITY_EDITOR
    [CustomEditor(typeof(Camera))]
    public class EditorSceneUI:Editor //OnSceneGUI为Editor的回调函数
    {
        //1.辅助UI,针对特定的选中物体,组件
        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的绘制要放在里面
                Handles.BeginGUI();
                GUI.backgroundColor = Color.red;
                if (GUILayout.Button("click", GUILayout.Width(200f)))
                {
                    Debug.LogFormat("click = {0}", camera.name);
                }
                GUILayout.Label("label");
                Handles.EndGUI();
            }
        }
    }
#endif

MenuItem

    //MenuItem
    //1.拓展全局自定义快捷键
    public class MenuItemClass {
        [MenuItem("Assets/HotKey %#d",false,-1)]
        static void HotKey()
        {
            Debug.Log("ctrl+shift+d");
        }
    }

在这里插入图片描述

EditorGUI

与GUI使用几乎一致,多用于编辑器开发,脚本中声明的public可序列化变量可以通过EditorGUI对其进行绘制。EditorGUILayout

   //EditorGUI
#if UNITY_EDITOR
    //1.常用EditorGUI
    [CustomEditor(typeof(myScripts))]
    public class EditorGUITest : Editor
    {
        private bool _EnableToggle;
        public override void OnInspectorGUI()
        {
            //获取脚本对象
            myScripts script = target as myScripts;
            script.myName = EditorGUILayout.TextField("text", script.myName); //一个string字段就是一个text
            script.myId = EditorGUILayout.IntField("int", script.myId); //int - int
            script.prefab = EditorGUILayout.ObjectField("GameObject", script.prefab, typeof(GameObject), true) as GameObject; //prefab-GameObject
            EditorGUILayout.BeginHorizontal();
            GUILayout.Button("1");
            GUILayout.Button("2");
            EditorGUILayout.EndHorizontal();
            script.myEnum = (myScripts.MyEnum)EditorGUILayout.EnumPopup("haha", script.myEnum);
            _EnableToggle = EditorGUILayout.BeginToggleGroup("我的TogGrp:", _EnableToggle);
            script.Tog1 = EditorGUILayout.Toggle("tog1:", script.Tog1);
            script.toggle2 = EditorGUILayout.Toggle("toggle2:", script.toggle2);
            EditorGUILayout.EndToggleGroup();
            EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeAge"), true); //以默认的样子绘制可序列化字段,一定要是加seralizefile特性的,否则找不到,会出错
            
            EditorGUILayout.HelpBox("这是一个帮助提示框", MessageType.Info);
            serializedObject.ApplyModifiedProperties(); //要数据保存才可以修改数据
        }
    }
    //2.EditorGUI样式(EditorStyles)
    public class EditorStylesTest:EditorWindow
    {
        //获得所有样式
        static List<GUIStyle> styles = null;

        static List<string> _IconNames = null;
        static Texture2D[] _Icons = null;
        //所有图标
        [MenuItem("Window/Show All Styles")]
        static void GetAllStyles()
        {
            _IconNames = new List<string>();
            _Icons = Resources.FindObjectsOfTypeAll<Texture2D>();
            for (int i = 0; i < _Icons.Length; i++)
            {
                Debug.unityLogger.logEnabled = false;//为了让他不报错,其他非unity内置资源提取不出来会报错
                GUIContent content = EditorGUIUtility.IconContent(_Icons[i].name); //不是unity内置的资源提取出来的content就会为null
                Debug.unityLogger.logEnabled = true;
                if(content!=null&&content.image!=null)
                {
                    _IconNames.Add(_Icons[i].name);
                }
            }
            EditorStylesTest window = (EditorStylesTest)EditorWindow.GetWindow<EditorStylesTest>("styles");
            styles = new List<GUIStyle>();
            foreach (var item in typeof(EditorStyles).GetProperties(BindingFlags.Static| BindingFlags.Public| BindingFlags.NonPublic)) //有些样式属性可能是私有的
            {
                object o = item.GetValue(null, null);
                if(o.GetType()==typeof(GUIStyle))
                {
                    styles.Add(o as GUIStyle);
                }
            }
            window.Show();
        }
        Vector2 scrollPos;
        Vector2 sp2;
        //绘制
        private void OnGUI()
        {
            //所有风格
            scrollPos = EditorGUILayout.BeginScrollView(scrollPos,false,false,GUILayout.Height(400)); //可选参数里面控制ScrollView的范围
            for (int i = 0; i < styles.Count; i++)
            {
                GUILayout.Label($"styles{i}:{styles[i].name}", styles[i]);
            }
            EditorGUILayout.EndScrollView();
            EditorGUILayout.Separator();
            //所有图标
            sp2 = EditorGUILayout.BeginScrollView(sp2, false, true);
            int m = (int)(position.width/50);
            for (int i = 0; i <_IconNames.Count; i++)
            {
                GUILayout.BeginHorizontal();
                for (int j = 0; j < m; j++)
                {
                    int index = i + j; //后面i增加了导致index可能大于length
                    if(index<_IconNames.Count)
                        GUILayout.Button(EditorGUIUtility.IconContent(_IconNames[index]), GUILayout.Width(50), GUILayout.Height(30)); //EditorGUIUtility.IconContent根据名字提取unity内置的资源
                }
                GUILayout.EndHorizontal();
            }
            EditorGUILayout.EndScrollView();
        }
    }
#endif

在这里插入图片描述
所有EditorGUI可以使用的样式
在这里插入图片描述
在这里插入图片描述

EditorWindow

    //EditorWindow
    public class EditorWindowTest:EditorWindow,IHasCustomMenu //继承IHasCustomMenu接口可以扩展window编辑窗口的右上角
    {
        private float _scrollFloat;
        [MenuItem("Window/Open My Window")]
        //初始化打开一个窗口(根据一个窗口类)
        static void Init()
        {
            EditorWindowTest window = (EditorWindowTest)EditorWindow.GetWindow(typeof(EditorWindowTest));
            window.Show();
        }
		//右上角按钮点击出来的
        public void AddItemsToMenu(GenericMenu menu)
        {
            menu.AddItem(new GUIContent("test1"), true, () => //默认勾选true
            {
                Debug.Log("test1");
            });
            menu.AddSeparator("Test/");
            menu.AddDisabledItem(new GUIContent("Disable"));
        }

        private void OnGUI()
        {
            //绘制在这里绘制
            GUILayout.Label("hello world!",EditorStyles.boldLabel);
            _scrollFloat = EditorGUILayout.Slider("slider", _scrollFloat, 0, 1);
        }
    }
发布了67 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Icecoldless/article/details/103707688