下面是一些简单的编辑器用到的函数,一般通过 MenuItem 来添加到菜单中。。。

在 Inspector 中字段的各种特性:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[Header( "My variables" )]
public string MyString;

[HideInInspector]
public string MyHiddenString;

[Multiline( 5 )]
public string MyMultilineString;

[TextArea( 2, 8 )]
public string MyTextArea;

[Space( 15 )]
public int MyInt;

[Range( 2.5f, 12.5f )]
public float MyFloat;

[Tooltip( "This is a tip for MyDouble" )]
public double MyDouble;

[SerializeField]
private double myHiddenDouble;

对组件的各种约束特性:

1
2
3
4
5
6
[DisallowMultipleComponent]
[RequireComponent( typeof( Rigidbody ) )]
public class AttributesExample : MonoBehaviour 
{

}

AssetDatabase.SaveAssets 之前需要 EditorUtility.SetDirty。 Prefab 中可能残留一些数据,导致通过 GetDependencies 获取依赖时被获取到,导致打包时导入了没用的资源,清洗方法: EditorUtility.SetDirty(obj); AssetDatabase.SaveAssets();

Onvalidate 函数用于检测 Inspector 上面变量值的变化

1
2
3
4
5
6
7
public class OnValidateExample : MonoBehaviour {
	public int size;
 
	void OnValidate() {
		Debug.Log("OnValidate");
	}
}

使用 Application.IsEditor 可以替代 #if UNITY_EDITOR,在 dll 中不识别 UNITY_EDITOR,所以用这个很好。

1
EditorApplication.isPlaying == (Application.isPlaying && Application.isEditor)
  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
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using System.IO;
using System;

public class EditorTool : MonoBehaviour {

    public static bool ShowDialog (string msg, string title = "提示", string button = "确定")
    {
        return EditorUtility.DisplayDialog (title, msg, button);
    }

    // 对话框
    public static void ShowDialogSelection (string msg, Action yesCallback)
    {
        if (EditorUtility.DisplayDialog ("确定吗", msg, "是!", "不!")) {
            yesCallback ();
        }
    }

    // 创建 Prefab,复制组件信息
    [MenuItem("Tools/CreatePrefab")]
    static void CreatePrefab()
    {
        string path = "Assets/test.prefab";
        Object obj = AssetDatabase.LoadMainAssetAtPath(path);
        GameObject go = new GameObject();
        if (obj == null)
        {
            go.AddComponent<GameData>();
            PrefabUtility.CreatePrefab(path, go);
        }
        else
        {
            GameObject o = obj as GameObject;
            foreach (var c in o.GetComponentsInChildren<MonoBehaviour>())
            {
                UnityEditorInternal.ComponentUtility.CopyComponent(c);
                UnityEditorInternal.ComponentUtility.PasteComponentAsNew(go);
                PrefabUtility.ReplacePrefab(go, obj);
            }
        }
        DestroyImmediate(go);
    }

    // 复制路径,使用 ctrl + c %_C)会导致其它复制内容失效,所以不能使用
    [MenuItem("Assets/CopyPath #_c", false, 100)]
    private static void CopyAssetPath()
    {
        if (Selection.activeObject != null)
        {
            string path = AssetDatabase.GetAssetPath(Selection.activeObject);
            EditorGUIUtility.systemCopyBuffer = path;
        }
    }

    // 打开场景
    [MenuItem("Tools/Open Main Scene %&i", false, 1)]
    public static void OpenMainScene()
    {
        var mainScene = "Assets/Game/Scene/Main.unity";
        UnityEditor.SceneManagement.EditorSceneManager.OpenScene(mainScene);
    }

    // 自动选择某个物体
    [MenuItem("Tools/Setting")]
    static void ShowSetting()
    {
        var obj = AssetDatabase.LoadMainAssetAtPath("Assets/Editor/EditorTool.cs");
        Selection.activeObject = obj;
        EditorGUIUtility.PingObject(obj);
        EditorApplication.ExecuteMenuItem("Window/Inspector");
    }

    // 打开目录
    [MenuItem("Tools/Open PC PersistentDataPath", false, 2)]
    public static void OpenPersistentDataPath()
    {
        System.Diagnostics.Process.Start(Application.persistentDataPath);
    }

    // 清理存储的数据
    [MenuItem("Tools/Clear PC PersistentDataPath", false, 3)]
    public static void ClearPersistentDataPath()
    {
        foreach (string dir in Directory.GetDirectories(Application.persistentDataPath))
        {
            Directory.Delete(dir, true);
        }
        foreach (string file in Directory.GetFiles(Application.persistentDataPath))
        {
            File.Delete(file);
        }
    }

    // 清理所有 PlayerPrefs
    [MenuItem("Tools/Clear Prefs", false, 4)]
    public static void ClearPlayerPrefs()
    {
        PlayerPrefs.DeleteAll();
        PlayerPrefs.Save();
        ShowDialog("Prefs Cleared!");
    }

    // 打开网页
    [MenuItem ("Tools/Open Url", false, 5)]
    public static void OpenUrl ()
    {
        System.Diagnostics.Process.Start ("https://www.baidu.com/");
    }
}

在NGUI中检查无效图片,还可以使用快捷键shift_c:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
[MenuItem ("FNW/检查无效图片 #_c", false, 6)]
static void CheckInvalidSprite ()
{
    if (Selection.activeGameObject != null) {
        GameObject obj = Selection.activeGameObject;

        foreach (var v in obj.GetComponentsInChildren<UISprite>(true)) {
            if (v.atlas == null || !v.isValid) {
                Debug.LogWarning ("无效的Sprite", v);
            }
        }

        foreach (var v in obj.GetComponentsInChildren<UITexture>(true)) {
            Debug.LogWarning ("无效的Texture", v);
        }
    }
}

如果希望在GameObject的脚本上右键弹出可执行菜单,可以在该脚本的函数上添加 ContextMenu,比如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[ContextMenu("LogAllAtlasName")]
public void LogAllAtlasName ()
{
    Debug.LogWarning ("[[[---Atlas: " + this.name);
    HashSet<UIAtlas> atlasContainer = new HashSet<UIAtlas> ();
    foreach (var v in this.GetComponentsInChildren<UISprite>(true)) {
        atlasContainer.Add (v.atlas);
    }
    foreach (var v in atlasContainer) {
        Debug.LogWarning (v);
    }
    Debug.LogWarning ("---]]]");
}

创建向导窗口要继承 ScriptableWizard

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

public class WizardTest : ScriptableWizard
{
    public float range = 500;
    public Color color = Color.red;

    [MenuItem("GameObject/New Light", false, 10)]
    static void CreateWizard()
    {
        // 创建向导窗口
        ScriptableWizard.DisplayWizard<WizardTest>("Create Light", "Create", "Apply");
    }

    private void OnWizardCreate()
    {
        GameObject go = new GameObject("New Light");
        Light lt = go.AddComponent<Light>();
        lt.range = range;
        lt.color = color;
    }

    private void OnWizardUpdate()
    {
        helpString = "Please set the color of the light";
    }

    private void OnWizardOtherButton()
    {
        if (Selection.activeTransform != null)
        {
            Light lt = Selection.activeTransform.GetComponent<Light>();
            if (lt != null)
            {
                lt.color = Color.green;
            }
        }
    }
}

在 Create 中创建菜单,用于创建asset资源文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class Test : MonoBehaviour {

	void Start () {
        // 创建asset资源
        var t = ScriptableObject.CreateInstance<AbilityData>();
        AssetDatabase.CreateAsset(t, "Assets/ability.asset");
    }
}

[CreateAssetMenu(fileName = "", menuName = "", order = 1)]
public class AbilityData : ScriptableObject
{
    public int ID;
    public int Name;
}

在编辑器扩展创建拖拽区域

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TriggerContainer : MonoBehaviour {

	void Start () {
		
	}
}
 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
using UnityEngine;
using System.Collections;

using UnityEditor;

// CustomEditor 允许自定义组件的编辑器显示
[CustomEditor (typeof(TriggerContainer))]
public class TriggerContainerEditor : Editor
{
    private SerializedObject obj;

    public void OnEnable ()
    {
        obj = new SerializedObject (target);
    }
 
    public override void OnInspectorGUI ()
    {
        DrawDefaultInspector ();
        EditorGUILayout.Space ();
        DropAreaGUI ();
    }
 
    public void DropAreaGUI ()
    {
        Event evt = Event.current;
        Rect drop_area = GUILayoutUtility.GetRect (0.0f, 50.0f, GUILayout.ExpandWidth (true));
        GUI.Box (drop_area, "Add Trigger");
     
        switch (evt.type) {
            case EventType.DragUpdated:
            case EventType.DragPerform:
                if (!drop_area.Contains (evt.mousePosition))
                    return;
             
                DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
         
                if (evt.type == EventType.DragPerform) {
                    DragAndDrop.AcceptDrag ();
             
                    foreach (Object dragged_object in DragAndDrop.objectReferences) {
                        // Do On Drag Stuff here
                        Debug.Log(dragged_object.name);
                    }
                }
                break;
        }
    }
}

在菜单 Edit -> Preferences 中添加新选项

 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;

#if UNITY_EDITOR
using UnityEditor;
#endif

public class CustomSetting
{
    public class SettingData
    {
        public bool needCheckTable = true;
    }

#if UNITY_EDITOR

    private static string settingkey = "customSetting";
    private static SettingData settingData;
    private static GUIContent needCheckTableContent = new GUIContent("needCheckTable");

    public static SettingData GetSetting()
    {
        CheckData();
        return settingData;
    }

    // PreferenceItem 允许在 Preference 菜单中创建菜单
    [PreferenceItem("CustomSetting")]
    private static void PreferenceGUI()
    {
        CheckData();
        settingData.needCheckTable = EditorGUILayout.Toggle(needCheckTableContent, settingData.needCheckTable);
        if (GUI.changed)
        {
            SaveData();
        }
    }

    private static void CheckData()
    {
        if (settingData == null)
        {
            if (EditorPrefs.HasKey(settingkey))
            {
                settingData = JsonUtility.FromJson<SettingData>(EditorPrefs.GetString(settingkey));
            }
            else
            {
                settingData = new SettingData();
                SaveData();
            }
        }
    }

    private static void SaveData()
    {
        EditorPrefs.SetString(settingkey, JsonUtility.ToJson(settingData));
    }

#endif
}

清除 Console Log

1
2
3
4
5
6
7
public void ClearLog()
{
    var assembly = Assembly.GetAssembly(typeof(UnityEditor.Editor));
    var type = assembly.GetType("UnityEditor.LogEntries");
    var method = type.GetMethod("Clear");
    method.Invoke(new object(), null);
}

Gizmos 用线画圆,如果是在 Editor 目录下可以用 UnityEditor.Handles.DrawWireDisc 方法替代

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
private void OnDrawGizmos()
{
    float meleeRadius = 3f;
    Gizmos.color = Color.white;
    float theta = 0;
    float x = meleeRadius * Mathf.Cos(theta);
    float y = meleeRadius * Mathf.Sin(theta);
    Vector3 pos = transform.position + new Vector3(x, 0, y);
    Vector3 newPos = pos;
    Vector3 lastPos = pos;
    for (theta = 0.1f; theta < Mathf.PI * 2; theta += 0.1f)
    {
        x = meleeRadius * Mathf.Cos(theta);
        y = meleeRadius * Mathf.Sin(theta);
        newPos = transform.position + new Vector3(x, 0, y);
        Gizmos.DrawLine(pos, newPos);
        pos = newPos;
    }
    Gizmos.DrawLine(pos, lastPos);
}

InitializeOnLoad 当编辑器启动或代码编译时自动执行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[InitializeOnLoad]
public class AttributesExample : MonoBehaviour
{
    
    static AttributesExample() 
    {
        [...]
    }

    [InitializeOnLoadMethod]
    private static void Foo()
    {
        [...]
    }
}

CustomPropertyDrawer 允许为组件在 Inspector 中自定义外观

1
2
3
4
5
[CustomPropertyDrawer( typeof( MyClass ) )]
public class AttributesExample : PropertyDrawer 
{
    
}

DrawGizmo 允许为组件创建自定义的 Gizmos

1
2
3
4
5
[DrawGizmo( GizmoType.Selected )]
private static void DoGizmo( AttributesExample obj, GizmoType type )
{
    
}