拓展编辑器

Unity提供了灵活多变的编辑器拓展API接口,通过代码反射,可以修改一些系统自带的编辑器窗口。此外,丰富的EditorGUI接口也可以拓展出各式各样的编辑器窗口。

拓展Project视图

属于编辑模式下的代码,需要放在Editor文件夹下;属于运行时执行的代码,放在任意非Editor文件夹下即可。Editor文件夹的位置比较灵活,它还可以作为多个目录的子文件夹存在,这样开发者就可以按功能来划分,将不同功能的编辑代码放在不同的Editor目录下。例如可以有多个Editor目录,它们各自处理各自的逻辑。

拓展右键菜单

选中资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;
using UnityEditor;

public class Script_03_01
{
[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);
}
}

Create按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;
using UnityEditor;

public class Script_03_02
{
[MenuItem("Assets/Create/My Create/Cube", false, 2)]
static void CreateCube()
{
GameObject.CreatePrimitive(PrimitiveType.Cube);
}

[MenuItem("Assets/Create/My Create/Sphere", false, 1)]
static void CreateSphere()
{
GameObject.CreatePrimitive(PrimitiveType.Sphere);
}
}

拓展布局

在右侧拓展自定义按钮,在代码中既可以设置拓展按钮的区域,也可监听按钮的点击事件。
在方法前面添加[InitializeOnLoadMethod]表示此方法会在C#代码每次编译完成后首先调用。监听EditorApplication.projectWindowItemOnGUI委托,即可使用GUI方法来绘制自定义的UI元素。GUI还提供了丰富的元素接口,可以用来添加文本、图片、滚动条和下拉框等复杂元素。

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 Script_03_03
{
[InitializeOnLoadMethod]
static void InitializeOnLoadMethod()
{
EditorApplication.projectWindowItemOnGUI = delegate (string guid, Rect selectionRect)
{
// 在Project视图中选择一个资源
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;
}
};
}
}

监听事件

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

public class Script_03_04 : UnityEditor.AssetModificationProcessor
{
[InitializeOnLoadMethod]
static void InitializeOnLoadMethod()
{
// 全局监听Project视图下的资源是否发生变化(添加、删除和移动)
EditorApplication.projectWindowChanged = delegate ()
{
Debug.Log("change");
};
}

// 监听“双击鼠标左键,打开资源”事件
public static bool IsOpenForEdit(string assetPath, out string message)
{
message = null;
Debug.LogFormat("open : {0}", assetPath);
// true表示该资源可以打开,false表示不允许在Unity中打开该资源
return true;
}

// 监听“资源即将被创建”事件
public static void OnWillCreateAsset(string path)
{
Debug.LogFormat("create : {0}", path);
}

// 监听“资源即将被保存”事件
public static string[] OnWillSaveAssets(string[] paths)
{
if (paths != null)
{
Debug.LogFormat("save : {0}", string.Join(",", paths));
}
return paths;
}

// 监听“资源即将被移动”事件
public static AssetMoveResult OnWillMoveAsset(string oldPath, string newPath)
{
Debug.LogFormat("move : {0} to {1}", oldPath, newPath);
// AssetMoveResult.DidMove表示该资源可以移动
return AssetMoveResult.DidMove;
}

// 监听“资源即将被删除”事件
public static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions option)
{
Debug.LogFormat("delete : {0}", assetPath);
// AssetDeleteResult.DidNotDelete表示该资源可以被删除
return AssetDeleteResult.DidNotDelete;
}
}

拓展Hierarchy视图

Hierarchy视图中出现的都是游戏对象,这些对象之间同样具有一定的关联关系。Hierarchy视图中的游戏对象会通过摄像机最终投影在发布的游戏中。

拓展菜单

1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;
using UnityEditor;

public class Script_03_05
{
[MenuItem("GameObject/My Create/Cube", false, 0)]
static void CreateCube()
{
GameObject.CreatePrimitive(PrimitiveType.Cube);
}
}

拓展布局

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 Script_03_06
{
[InitializeOnLoadMethod]
static void InitializeOnLoadMethod()
{
EditorApplication.hierarchyWindowItemOnGUI = delegate (int instanceID, Rect selectionRect)
{
// 在Hierarchy视图中选择一个资源
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);
}
}
};
}
}

重写菜单

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

public class Script_03_07
{
[MenuItem("Window/Test/Test")]
static void Test()
{
}

[MenuItem("Window/Test/Test1")]
static void Test1()
{
}

[MenuItem("Window/Test/Demo/Test2")]
static void Test2()
{
}

[InitializeOnLoadMethod]
static void StartInitializeOnLoadMethod()
{
EditorApplication.hierarchyWindowItemOnGUI += OnHierarchyGUI;
}

static void OnHierarchyGUI(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;
// 这里可以判断selectedGameObject的条件
if (selectedGameObject)
{
Vector2 mousePosition = Event.current.mousePosition;

EditorUtility.DisplayPopupMenu(new Rect(mousePosition.x, mousePosition.y, 0, 0), "Window/Test", null);
Event.current.Use();
}
}
}
}

在上述代码中,使用Event.current来获取当前的事件。当监听到鼠标抬起的事件后,并且满足游戏对象的选中状态,开始执行自定义事件。其中,EditorUtility.DisplayPopupMenu用于弹出自定义菜单,Event.current.Use()的含义是不再执行原有的操作,所以就实现了重写菜单。

此外,Hierarchy视图还可以重写系统自带的菜单行为。例如,觉得Unity创建的Image组件不好,可以复写它的行为。创建Image组件时,会自动勾选RaycastTarget。如果图片不需要处理点击事件,这样会带来一些额外的开销。下面的代码让RaycastTarget默认不勾选。由于重写了菜单,所以需要通过脚本自行创建Image对象和组件。接着,获取到image组件对象,直接设置它的raycastTarget属性即可。

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

public class Script_03_08
{
[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视图

拓展源生组件

摄像机就是典型的源生组件。可以在摄像机组件的最上面添加一个按钮。它的局限性就是拓展组件只能加在源生组件的最上面或者最下面。

1
2
3
4
5
6
7
8
9
10
11
12
13
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(Camera))]
public class Script_03_09 : Editor
{
public override void OnInspectorGUI()
{
if (GUILayout.Button("拓展按钮"))
{ }
base.OnInspectorGUI();
}
}

拓展继承组件

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 Script_03_10 : Editor
{
private Editor m_Editor;
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();
//base.OnInspectorGUI();
}
}

组件不可编辑

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;
using System.Reflection;

[CustomEditor(typeof(Transform))]
public class Script_03_11 : Editor
{
private Editor m_Editor;
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("拓展按钮下"))
{

}
}
}
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 Script_03_12
{
[MenuItem("GameObject/3D Object/Lock/Lock", false, 0)]
static void Lock()
{
if (Selection.gameObjects != null)
{
foreach (var gameObject in Selection.gameObjects)
{
gameObject.hideFlags = HideFlags.NotEditable;
}
}
}

[MenuItem("GameObject/3D Object/Lock/UnLock", false, 1)]
static void UnLock()
{
if (Selection.gameObjects != null)
{
foreach (var gameObject in Selection.gameObjects)
{
gameObject.hideFlags = HideFlags.None;
}
}
}
}

Context菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using UnityEngine;
using UnityEditor;

public class Script_03_13
{
[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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class Script_03_14 : MonoBehaviour
{
public string contextName;

#if UNITY_EDITOR
[MenuItem("CONTEXT/Script_03_14/New Context 1")]
public static void NewContext2(MenuCommand command)
{
Script_03_14 script = (command.context as Script_03_14);
script.contextName = "hello world!";
}
#endif
}

拓展Scene视图

辅助元素

1
2
3
4
5
6
7
8
9
10
11
12
13
using UnityEngine;

public class Script_03_15 : MonoBehaviour
{
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
// 画线
Gizmos.DrawLine(transform.position, Vector3.one);
// 立方体
Gizmos.DrawCube(Vector3.one, Vector3.one);
}
}

辅助UI

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 Script_03_16 : 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

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 Script_03_17 : Editor
{
[InitializeOnLoadMethod]
static void InitializeOnLoadMethod()
{
SceneView.onSceneGUIDelegate = delegate (SceneView sceneView)
{
Handles.BeginGUI();

GUI.Label(new Rect(0f, 0f, 50f, 15f), "标题");
GUI.Button(new Rect(0f, 20f, 50f, 50f), AssetDatabase.LoadAssetAtPath<Texture>("Assets/unity.png"));

Handles.EndGUI();
};
}
}

禁用选中对象

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

public class Script_03_18
{
[InitializeOnLoadMethod]
static void InitializeOnLoadMethod()
{
SceneView.onSceneGUIDelegate = delegate (SceneView sceneView)
{
Event e = Event.current;
if (e!= null)
{
int controlID = GUIUtility.GetControlID(FocusType.Passive);
if (e.type == EventType.Layout)
{
HandleUtility.AddDefaultControl(controlID);
}
}
};
}
}

拓展Game视图

MenuItem菜单

面板拓展

脚本挂在游戏对象上时,右侧会出现它的详细信息面板,这些信息是根据脚本中声明的public可序列化变量而来的。此外,也可以通过EditorGUI来对它进行绘制,让面板更具可操作性。

Inspector面板

EditorGUI和GUI的用法几乎完全一致,目前来说前者多用于编辑器开发,后者多用于发布后调试编辑器。总之,它们都是起辅助作用的。EditorGUI提供的组件非常丰富,常用的绘制元素包括文本、按钮、图片和滚动框等。做一个好的编辑器,是离不开EditorGUI的。如下图所示,将EditorGUI拓展在Inspector面板上了。

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
using UnityEngine;
using UnityEditor;
using UnityEditor.Experimental.UIElements;
using UnityEngine.Experimental.UIElements;
using UnityEngine.Experimental.UIElements.StyleEnums;

public class Script_03_33 : EditorWindow
{
[MenuItem("UIElementsTest/Style")]
public static void ShowExample()
{
Script_03_33 window = GetWindow<Script_03_33>();
window.titleContent = new GUIContent("Script_03_33");
}

public void OnEnable()
{
var root = this.GetRootVisualContainer();
// 添加style.uss样式
root.AddStyleSheetPath("style");

var boxes = new VisualContainer();
// 设置自动换行
boxes.style.flexDirection = FlexDirection.Row;
boxes.style.flexWrap = Wrap.Wrap;
for (int i=0;i<20;i++)
{
TextField m_TextField = new TextField();
boxes.Add(m_TextField);
Button button = new Button(delegate () {
Debug.LogFormat("Click");
});
button.text = "我是按钮我要自适应";
boxes.Add(button);
}
root.Add(boxes);
}
}

在上述代码中,将脚本部分和Editor部分的代码合在一个文件中。如果需要拓展的面板比较复杂,建议分成两个文件存放,一个是脚本,另一个是Editor脚本。

EditorWindows窗口

Unity提供编辑器窗口,开发者可以自由拓展自己的窗口。Unity编辑器系统自带的视图窗口其实也是用EditorWindows实现的。如下图所示,它绘制元素时同样适用EditorGUI代码。

使用EditorWindow.GetWindow()方法即可打开自定义窗口,在OnGUI()方法中可以绘制窗口元素。注意代码中EditorWindos窗口的生命周期。

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

public class Script_03_24 : EditorWindow
{
[MenuItem("Window/Open My Window")]
static void Init()
{
Script_03_24 window = (Script_03_24)EditorWindow.GetWindow(typeof(Script_03_24));
window.Show();
}

private Texture m_MyTexture = null;
private float m_MyFloat = 0.5f;

private void Awake()
{
Debug.LogFormat("窗口初始化时调用");
m_MyTexture = AssetDatabase.LoadAssetAtPath<Texture>("Assets/unity.png");
}

private void OnGUI()
{
GUILayout.Label("Hello World!!", EditorStyles.boldLabel);
m_MyFloat = EditorGUILayout.Slider("Slider", m_MyFloat, -5, 5);
GUI.DrawTexture(new Rect(0, 30, 100, 100), m_MyTexture);
}

private void OnDestroy()
{
Debug.LogFormat("窗口销毁时调用");
}

private void OnFocus()
{
Debug.LogFormat("窗口拥有焦点时调用");
}

private void OnHierarchyChange()
{
Debug.LogFormat("Hierarchy视图发生改变时调用");
}

private void OnInspectorUpdate()
{
//Debug.LogFormat("Inspector每帧更新");
}
private void OnLostFocus()
{
Debug.LogFormat("失去焦点");
}

private void OnProjectChange()
{
Debug.LogFormat("Project视图发生改变时调用");
}

private void OnSelectionChange()
{
Debug.LogFormat("在Hierarchy或者Project视图中选择一个对象时调用");
}

private void Update()
{
// Debug.LogFormat("每帧更新");
}
}

EditorWindows下拉菜单

在EditorWindows编辑窗口的右上角,有个下拉菜单,也可以对该菜单中的选项进行拓展,不过这里需要实现IHasCustomMenu接口。

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

public class Script_03_25 : EditorWindow, IHasCustomMenu
{
void IHasCustomMenu.AddItemsToMenu(GenericMenu menu)
{
menu.AddDisabledItem(new GUIContent("Disable"));
menu.AddItem(new GUIContent("Test1"), true, () =>
{
Debug.Log("Test1");
});
menu.AddItem(new GUIContent("Test2"), true, () =>
{
Debug.Log("Test2");
});
menu.AddSeparator("Test/");

menu.AddItem(new GUIContent("Test/Test3"), true, () =>
{
Debug.Log("Test3");
});
}

[MenuItem("Window/Open My Window")]
static void Init()
{
Script_03_25 window = (Script_03_25)EditorWindow.GetWindow(typeof(Script_03_25));
window.Show();
}
}

上述代码中,通过AddItem()方法来添加列表元素,并且监听选择后的事件。

预览窗口

选择游戏对象或者游戏资源后,Inspector面板下方将会出现它的预览窗口,但是有些资源是没有预览信息的,不过可以监听它的窗口方法来重新绘制它。如下图所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;
using UnityEditor;

[CustomPreview(typeof(GameObject))]
public class Script_03_26 : ObjectPreview
{
public override bool HasPreviewGUI()
{
return true;
}

public override void OnPreviewGUI(Rect r, GUIStyle background)
{
GUI.DrawTexture(r, AssetDatabase.LoadAssetAtPath<Texture>("Assets/unity.png"));
GUILayout.Label("Hello World!!!");
}
}

这段代码的原理就是继承ObjectPreview并且重写OnPreviewGUI()方法,接着就可以通过代码进行绘制了。[CustomPreview(typeof(GameObject))]中的GameObject代表需要重新绘制的预览对象,也可以换成别的系统对象或自定义的脚本对象。

获取预览信息

有些资源是有预览信息的,比如模型资源。在预览窗口中,可以看到它的样式。如果需要在自定义窗口中显示它,就需要获取它的预览信息。如下图所示,选择一个游戏对象后,会在自定义窗口中显示它。

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 Script_03_27 : EditorWindow
{
private GameObject m_MyGo;
private Editor m_MyEditor;

[MenuItem("Window/Open My Window")]
static void Init()
{
Script_03_27 window = (Script_03_27)EditorWindow.GetWindow(typeof(Script_03_27));
window.Show();
}

private void OnGUI()
{
// 设置一个游戏对象
m_MyGo = (GameObject)EditorGUILayout.ObjectField(m_MyGo, typeof(GameObject), true);

if (m_MyGo != null)
{
if (m_MyEditor == null)
{
// 创建Editor实例
m_MyEditor = Editor.CreateEditor(m_MyGo);
}
// 预览它
m_MyEditor.OnPreviewGUI(GUILayoutUtility.GetRect(500, 500), EditorStyles.whiteLabel);
}
}
}

在上述代码中,预览对象首先需要通过Editor.CreateEditor()拿到它的Editor实例对象,接着调用OnPreviewGUI()方法传入窗口的显示区域。

Unity编辑器的源码

Unity编辑器几乎都是用C#编写而成,视图中也大量使用EditorGUI来编辑布局。例如,对于常见的5大布局视图,所有的代码都放在UnityEditor.dll中。打开Unity安装目录,在Unity\Editor\Data\Managed子目录中存放着引擎所需要用到的所有DLL文件。

查看DLL

拿到UnityEditor.dll以后,就可以通过第三方工具来分析和查看了。常用的工具包括.NET Reflector以及ILSpy。其实Unity的C#版API接口都在UnityEngine.dll里,只是源码的核心功能都是在C/C++中完成的,DLL只负责中间调用接口而已。

清空控制台日志

系统日志以及Debug.Log()产出的日志都输出在Console窗口中。在Console窗口的左上角,有个Clean按钮,它用于清空控制台日志。如果希望脚本可以灵活自动清空日志,就必须使用反射了。首先找到控制台的窗口类(ConsoleWindows.cs),接着在OnGUI()方法中可以看到:点击Clean按钮后,Unity会执行LogEntries.Clear()方法。

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;
using System.Reflection;

public class Script_03_28
{
[MenuItem("Tools/CreateConsole")]
static void CreateConsole()
{
Debug.Log("CreateConsole");
}

[MenuItem("Tools/CleanConsole")]
static void CleanConsole()
{
// 获取assembly
Assembly assembly = Assembly.GetAssembly(typeof(Editor));
// 反射获取LogEntries对象
MethodInfo methodInfo = assembly.GetType("UnityEditor.LogEntries").GetMethod("Clear");
// 反射调用它的Clear方法
methodInfo.Invoke(new object(), null);
}
}

获取EditorStyles样式

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

public class Script_03_29 : EditorWindow
{
static List<GUIStyle> styles = null;

[MenuItem("Window/Open My Window")]
public static void Test()
{
EditorWindow.GetWindow<Script_03_29>("styles");

styles = new List<GUIStyle>();

foreach (PropertyInfo fi in typeof(EditorStyles).GetProperties(BindingFlags.Static|BindingFlags.Public|BindingFlags.NonPublic))
{
object o = fi.GetValue(null, null);
if (o.GetType() == typeof(GUIStyle))
{
styles.Add(o as GUIStyle);
}
}
}

public Vector2 scrollPosition = Vector2.zero;

private void OnGUI()
{
scrollPosition = GUILayout.BeginScrollView(scrollPosition);
for (int i =0;i<styles.Count;i++)
{
GUILayout.Label("EditorStyles." + styles[i].name, styles[i]);
}
GUILayout.EndScrollView();
}
}

获取内置图标样式

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

public class Script_03_30 : EditorWindow
{
[MenuItem("Window/Open My Window")]
public static void OpenMyWindow()
{
EditorWindow.GetWindow<Script_03_30>("icons");
}

private Vector2 m_Scroll;
private List<string> m_Icons = null;
private void Awake()
{
m_Icons = new List<string>();
Texture2D[] t = Resources.FindObjectsOfTypeAll<Texture2D>();
foreach (Texture2D x in t)
{
Debug.unityLogger.logEnabled = false;
GUIContent gc = EditorGUIUtility.IconContent(x.name);
Debug.unityLogger.logEnabled = true;
if (gc != null && gc.image != null)
{
m_Icons.Add(x.name);
}
}
Debug.Log(m_Icons.Count);
}

private void OnGUI()
{
m_Scroll = GUILayout.BeginScrollView(m_Scroll);
float width = 50f;
int count = (int)(position.width / width);
for (int i=0;i<m_Icons.Count;i+=count)
{
GUILayout.BeginHorizontal();
for (int j=0;j<count;j++)
{
int index = i + j;
if (index<m_Icons.Count)
{
GUILayout.Button(EditorGUIUtility.IconContent(m_Icons[index]), GUILayout.Width(width), GUILayout.Height(30));
}
}
GUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
}
}

拓展默认面板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(UnityEditor.DefaultAsset))]
public class Script_03_31 : Editor
{
public override void OnInspectorGUI()
{
string path = AssetDatabase.GetAssetPath(target);
GUI.enabled = true;
if (path.EndsWith(string.Empty))
{
GUILayout.Label("拓展文件夹");
GUILayout.Button("我是文件夹");
}
}
}

UIElements

1
2
3
4
5
6
7
8
9
10
11
TextField
{
height:100;
width:100px;
}

Button
{
height:50;
width:100px;
}
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
using UnityEngine;
using UnityEditor;
using UnityEditor.Experimental.UIElements;
using UnityEngine.Experimental.UIElements;
using UnityEngine.Experimental.UIElements.StyleEnums;

public class Script_03_33 : EditorWindow
{
[MenuItem("UIElementsTest/Style")]
public static void ShowExample()
{
Script_03_33 window = GetWindow<Script_03_33>();
window.titleContent = new GUIContent("Script_03_33");
}

public void OnEnable()
{
var root = this.GetRootVisualContainer();
// 添加style.uss样式
root.AddStyleSheetPath("style");

var boxes = new VisualContainer();
// 设置自动换行
boxes.style.flexDirection = FlexDirection.Row;
boxes.style.flexWrap = Wrap.Wrap;
for (int i=0;i<20;i++)
{
TextField m_TextField = new TextField();
boxes.Add(m_TextField);
Button button = new Button(delegate () {
Debug.LogFormat("Click");
});
button.text = "我是按钮我要自适应";
boxes.Add(button);
}
root.Add(boxes);
}
}
0%