MenuItem(string itemName, bool isValidateFunction, int priority)

  • “itemName:菜单名称路径
  • “isValidateFunction:默认为false,如果为true则是一个验证函数,方法返回值必须是bool类型 表示是否可以执行这个命令
  • “priority:菜单项显示排序 如果相差间隔大于=11 就会有一个下划线
  • “快捷键 %(ctrl)、# (shift)、& (alt) _w(按下w, 单一字母)

参考:

Unity Editor 基础篇(十一):MenuItem属性_庸人自扰Eam的博客-CSDN博客_unity menuitem

Unity Editor自定义菜单排序(MenuItem Order) - 赵青青 - 博客园 (cnblogs.com)

Unity SerializedProperty和Target 区别

优点 缺点
SerializedProperty 1.可以多选
2.自动Undo
3.预制体重写
4.可以修改私有对象
比较麻烦
Target 简单好用,跟调用对象一样 不能修改私有变量 不能多选 不能undo 修改对象后,要使用EditorUtility.SetDirty(target) 将target标记为脏状态,否则修改状态不会被记

示例代码:

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
[CustomEditor(typeof(InspectorExample))]
[CanEditMultipleObjects]        //用于处理多选
public class InspectorExampleEditor : Editor
{
    private SerializedProperty hpProp;
    private void OnEnable()
    {
        hpProp = serializedObject.FindProperty("Hp");
    }

    public override void OnInspectorGUI()
    {
        //1. 使用SerializedProperty 优点:自动处理多选、Undo、预制件重写
        serializedObject.Update();
        EditorGUILayout.IntSlider(hpProp, 0100, hpContent);
        //当没有多选不同值时就显示ProgressBar
        if (!hpProp.hasMultipleDifferentValues)   
            ProgressBar(hpProp.intValue / 100f"HP");
        EditorGUILayout.IntSlider(mpProp, 0100, mpContent);
        if (!mpProp.hasMultipleDifferentValues)
            ProgressBar(mpProp.intValue / 100f"MP");
        //可以直接通过PropertyField绘制默认Field或自定义PropertyAttribte
        EditorGUILayout.PropertyField(swordProp, swordContent);

        //应用SerializedProperty改变
        serializedObject.ApplyModifiedProperties();

        //2. 直接修改对象身上的值 
        EditorGUI.BeginChangeCheck();
        InspectorExample iEx = (InspectorExample) target;
        iEx.Attack = EditorGUILayout.IntSlider(attackContent, iEx.Attack, 0100);
        bool allowSceneObject = !EditorUtility.IsPersistent(target);
        iEx.Shield =
            (GameObject) EditorGUILayout.ObjectField("Shield", iEx.Shield, typeof(GameObject), allowSceneObject);
        if (EditorGUI.EndChangeCheck())
        {
            //需要标记target为脏
            EditorUtility.SetDirty(target);
            // EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
        }
    }

}

窗口扩展

Tools工具扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using UnityEditor;
using UnityEngine;
using UnityEditor.EditorTools;
/// <summary>
/// ____DESC: Tools(经常操作的工具)工具扩展 https://docs.unity.cn/cn/2020.3/ScriptReference/EditorTools.EditorTool.html
/// </summary>

public class EditorToolsExtender
{
//[EditorTool("AA")]
//class A : EditorTool
//{
// private void OnEnable()
// {
// Debug.Log("aaa");
// }
// public override void OnToolGUI(EditorWindow window)
// {
// base.OnToolGUI(window);
// }
//}
}

HierarchyWindow

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
using UnityEditor;
using UnityEngine;
/// <summary>
/// ____DESC: Hierarchy窗口 扩展器
/// </summary>
[InitializeOnLoad]
public class HierarchyWindowExtender
{
//静态构造
static HierarchyWindowExtender()
{
EditorApplication.hierarchyWindowItemOnGUI += OnHierarchyGUI;
}

static void OnHierarchyGUI(int instanceID, Rect selectionRect)
{
float size = 18;
Rect rect = new Rect(selectionRect.x + selectionRect.width - size * 1.2f, selectionRect.y, size+10, size);
if ((EditorUtility.InstanceIDToObject(instanceID) is GameObject))
{
//if ((EditorUtility.InstanceIDToObject(instanceID) as GameObject).TryGetComponent<TriggerEvent>(out TriggerEvent go))
//{
// if (go.isTriggerToDialogDubbing)
// GUI.Label(rect, dialogTexture);
//}
//显示SpriteRenderer的sortingOrder
if ((EditorUtility.InstanceIDToObject(instanceID) as GameObject).TryGetComponent<UnityEngine.SpriteRenderer>(out UnityEngine.SpriteRenderer sp))
{
GUI.color = Color.green;
GUI.Label(rect, sp.sortingOrder.ToString());
GUI.color = Color.white;
}
//else if ((EditorUtility.InstanceIDToObject(instanceID) as GameObject).TryGetComponent<MeshRenderer>(out MeshRenderer mr))
//{
// GUI.color = Color.green;
// GUI.Label(rect, mr.sortingOrder.ToString());
// GUI.color = Color.white;
//}
}
}
}

InspectorWindwow

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
using System.Collections;
using UnityEngine;
using UnityEditor;
/// <summary>
/// ____DESC: Inspector 窗口扩展
/// </summary>

public class InspectorWindwowExtender
{
//[InitializeOnLoadMethod]
static void ButtonTodo()
{
// Editor.finishedDefaultHeaderGUI += Editor_finishedDefaultHeaderGUI;
}

private static void Editor_finishedDefaultHeaderGUI(Editor obj)
{
//GameObject go = obj.target as GameObject;
//if (go)
//{
// if (go.TryGetComponent<SkeletonAnimation>(out var spine))
// {
// if (GUILayout.Button("更换Spine材质为2d 光类型"))
// {
// var mr = spine.GetComponent<MeshRenderer>();
// mr.sharedMaterial.shader = Shader.Find("Universal Render Pipeline/2D/Spine/Sprite");
// mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
// mr.lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off;
// mr.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off;
// spine.addNormals = true;
// }
// }
}
}

ProjectWindow

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 System.Collections;
using UnityEngine;
using UnityEditor;

/// <summary>
/// ____DESC: 文件描述
/// </summary>
[InitializeOnLoad]
public class ProjectWindowExtender
{
static ProjectWindowExtender()
{
//EditorApplication.projectWindowItemOnGUI += OnGUI;
}
static void OnGUI(string guid, Rect selectionRect)
{
//if (Event.current.type != EventType.ContextClick) return;
//Event.current.Use();
//// return;
//var genericMenu = new GenericMenu();
//genericMenu.AddItem(new GUIContent("MenuItem1"), false, null);
//genericMenu.AddItem(new GUIContent("MenuItem2"), false, null);
//genericMenu.ShowAsContext();
}

SceneWindow

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 System.Collections;
using UnityEditor;
using UnityEngine;
/// <summary>
/// ____DESC: Scene窗口扩展
/// </summary>
public class SceneWindowExtender
{
[InitializeOnLoadMethod]
static void Init()
{
//SceneView.duringSceneGui -= SceneView_duringSceneGui;
// SceneView.duringSceneGui += SceneView_duringSceneGui;
}

private static void SceneView_duringSceneGui(SceneView obj)
{
/*eg:
*Handles场景视图中的自定义 3D GUI 控件和绘制操作
*Handles.BeginGUI();
*GUILayout.Button("aa",GUILayout.Width(50));
*Handles.EndGUI();
*/
}
}

其他记录

ProjectWindowUtil

EndNameEditAction当完成文件命名 需要创建类继承它

SettingsService 打开命令路径
SettingsService.OpenProjectSettings("Project/Player");//打开PlayerSetging窗口界面
EditorBuildSettings获取编辑器场景设置
EditorUtility编辑器工具 打开文件 显示对话框
EditorApplication.ExecuteMenuItem 编辑器执行某个菜单命令

IHasCustomMenu 继承了这个接口 给编辑器”…”添加菜单

  • EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight)

  • GUILayoutUtility.GetRect

    可获取当前绘制的区域

    1
    2
    var rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight);
    EditorGUI.ProgressBar(rect, 0.5f, "progress");
  • EditorStyles.label.CalcSize(new GUIContent(go.name))

    获取当前label的尺寸