大家好,我是阿赵,继续分享Unity编辑器界面扩展。
这一篇会举一些编辑器UI的具体例子,然后分享一下怎样使用Unity编辑器内置的资源和风格。
一、 自定义窗体综合小例子
之前一篇简单了介绍了怎样创建一个自定义的编辑器窗体,介绍了一些EditorWindow的基础知识,这里写一个小例子:
using UnityEditor;
using UnityEngine;
public class TestEditorWindow : EditorWindow
{
[MenuItem("Tools/TestWindow")]
private static void ShowTestWindow()
{
TestEditorWindow win = GetWindow<TestEditorWindow>();
win.titleContent = new GUIContent("测试窗体");
win.maxSize = win.minSize = new Vector2(500, 550);
}
private string[] platformList = new string[] { "d_BuildSettings.Switch", "d_BuildSettings.PS5", "d_BuildSettings.XboxOne", "d_BuildSettings.Metro", "d_BuildSettings.iPhone", "d_BuildSettings.Android" };
private Vector2 platformScrollPos = new Vector2();
private int iconSize = 100;
private string filePathStr = "";
private string searchStr = "";
private Color selectedColor = Color.white;
private Object selectedObj;
private string[] popupStrs = new string[] {"选项一","选项二","选项三","选项四" };
private int popupIndex = 0;
void OnGUI()
{
GUILayout.Box(EditorGUIUtility.IconContent("azhaoLogo"), GUILayout.Width(512), GUILayout.Height(128));
GUILayout.BeginHorizontal(EditorStyles.helpBox,GUILayout.Width(500));
GUILayout.Label("请选择文件路径:", EditorStyles.whiteLargeLabel, GUILayout.Width(100));
filePathStr = EditorGUILayout.TextField(filePathStr, GUILayout.Width(300), GUILayout.Height(30));
if (GUILayout.Button(EditorGUIUtility.IconContent("d_OpenedFolder Icon"), GUILayout.Width(40), GUILayout.Height(30)))
{
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("请搜索:", EditorStyles.linkLabel, GUILayout.Width(100));
searchStr = EditorGUILayout.TextField(searchStr, EditorStyles.toolbarSearchField, GUILayout.Width(300));
GUILayout.EndHorizontal();
GUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Width(500));
EditorGUILayout.HelpBox("请问您习惯在哪个平台上玩游戏:",MessageType.Info);
platformScrollPos = GUILayout.BeginScrollView(platformScrollPos,GUILayout.Height(120));
GUILayout.BeginHorizontal();
for (int i = 0;i<platformList.Length;i++)
{
if(GUILayout.Button(EditorGUIUtility.IconContent(platformList[i]),GUILayout.Width(iconSize),GUILayout.Height(iconSize)))
{
//do something
}
}
GUILayout.EndHorizontal();
GUILayout.EndScrollView();
GUILayout.EndVertical();
EditorGUILayout.HelpBox("请不要说谎,我知道你在想什么", MessageType.Error);
GUILayout.BeginHorizontal();
GUILayout.Label("请选择物体:", EditorStyles.largeLabel, GUILayout.Width(100));
selectedObj = EditorGUILayout.ObjectField(selectedObj, typeof(Object), true, GUILayout.Width(200));
GUILayout.EndHorizontal();
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.Label("请选择颜色:", EditorStyles.linkLabel, GUILayout.Width(100));
selectedColor = EditorGUILayout.ColorField(selectedColor, GUILayout.Width(200));
GUILayout.EndHorizontal();
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.Label("下拉列表:", GUILayout.Width(100));
popupIndex = EditorGUILayout.Popup(popupIndex, popupStrs, GUILayout.Width(200));
GUILayout.EndHorizontal();
}
}
运行窗体,会发现现在的窗体是这样的:
这个窗体比较简单,但里面用到了一些Unity编辑器扩展里面特有的东西,比如内置图标、自定义图片、编辑器UI风格和特殊组件。
二、 Unity内置图标
1、内置图标的使用
在上面的例子里面,出现了很多图标,比如各个游戏平台的图标、文件夹的图标和自定义的”阿赵Unity工具”的标题图片。这些图标是怎样调用的呢?
从代码上看,是通过了
EditorGUIUtility.IconContent
这个API来获得对应的图标图片的,传入图片的名字,不需要带扩展名,就可以获得相应的图片。
这个方法,是通过读取Assets/Editor Default Resources/Icons这个路径来获得对应的图片的。这个Editor Default Resources文件夹,在项目里面是看不到的,我们可以自己在Assets文件夹下面建立这个文件夹,然后把图片放到里面。比如我的那个”阿赵Unity工具”标题图片,就是放在这个文件夹里面的。
但有很多图片并不需要自己放到这个Assets/Editor Default Resources/Icons文件夹里面,也同样获取得到,比如平台图标"d_BuildSettings.Switch"或者文件夹图标"d_OpenedFolder Icon"。这是因为,Unity编辑器已经内置了一些可以使用的自带的图标,我们可以直接使用的。
2、 获取所有能用的内置图片名称
既然Unity有很多默认的图标可以使用,那我们怎么知道这些图标的名字呢?比如刚才说的"d_BuildSettings.Switch",是怎么知道的呢?
这里我做了个工具,可以获取所有的内置图标,并且点击的时候,会打印图标的名称并且复制到剪切板:
还可以搜索自己想要的关键词:
这些内置的资源是怎样获取的呢?
在网上很多人介绍,说通过Resrouces来获取,比如这样
Texture[] textures = Resources.FindObjectsOfTypeAll<Texture>();
这种方法可以获取一部分的图标,但很多图标是获取不到的,看看Resources.FindObjectsOfTypeAll的API说明:
这个方法只会获取Unity编辑器已经加载了的资源,这会导致一个问题,有一些图标明明能用,但由于没有在Unity编辑器打开过对应的界面显示出来,就获取不到。所以这个方法其实是不行的。
那么真正能获取到所有的icon的名称呢?首先,我们要知道这些资源是存在在哪里的。
先找到Unity的安装目录,然后找到Editor/Data/Resources/unity editor resources文件
这个文件里面就有我们需要的资源和名称了。
这个文件我们打开看会很混乱,不过我们的目的不是完全解析这个文件,而只是想提取出里面的资源名称而已,所以接下来我会用正则表达式来获取。
首先要知道里面代表资源的表达方式是什么,根据我的观察,里面的资源有2种声明方式:
第1种:
icons/xxxx.png
第2种:
icons/processed/xxxx.asset
其中xxxx就是图标的名称。
于是可以分别通过2个正则表达式来匹配:
(?<=icons/)[^\.]+(?=.png)
(?<=icons/processed/)[^\.]+(?=.asset)
把这些匹配到的名字,保存到一个txt里面,这样就一劳永逸了。我这里随便先保存到了Editor文件夹里面。
接下来的事情就简单了,通过API
EditorGUIUtility.IconContent
我们把之前收集到的icon名字列表逐个获取一下,就能把所有的icon获取到了。
三、 Unity内置风格
在上面的例子里面,用到了一些特殊的UI显示风格,一般是通过EditorStyles来指定的。
EditorStyles有多少种,不需要我们去猜,因为API上面有写,各位可以自己去查一下:
不过API上面的文字是不形象的,也没有附上效果说明,所以我们不知道具体的显示效果。
于是我也做了个预览工具:
同样也是可以通过搜索来找到自己想要的内容。
四、 完整工具源码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Reflection;
public class BuiltInResViewTool : EditorWindow
{
[MenuItem("Tools/显示内置资源")]
static void ShowWin()
{
BuiltInResViewTool _instance = GetWindow<BuiltInResViewTool>("Unity内置资源");
_instance.minSize = _instance.maxSize = new Vector2(800, 600);
}
#region icon
private string unityEditorResourcesPath = "";
private List<GUIContent> iconContentList;
private List<GUIContent> showIconContentList;
private Vector2 iconScrollPosition = Vector2.zero;
private int iconSize = 40;
private int rowCount = 18;
private string iconFilterStr = "";
private int builtInIconListExist = -1;
#endregion
#region style
private List<GUIStyle> guiStyles;
private Vector2 stylesScrollPosition = Vector2.zero;
private string styleFilterStr = "";
private List<GUIStyle> showGuiStyleList;
#endregion
private int titleSelectIndex = 0;
private string[] titleStrs = new string[] { "图标", "Style" };
private void OnGUI()
{
titleSelectIndex = GUILayout.SelectionGrid(titleSelectIndex, titleStrs, 2, GUILayout.Width(200));
if(titleSelectIndex == 0)
{
DrawIcons();
}
else if(titleSelectIndex == 1)
{
DrawStyle();
}
}
#region icon
/// <summary>
/// 绘制Icon列表
/// </summary>
private void DrawIcons()
{
ShowLine("内置Icon");
GUIStyle style = new GUIStyle();
style.normal.textColor = Color.yellow;
GUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label("如果没有显示icon,请找到Unity的安装目录,并选择Editor/Data/Resources/unity editor resources文件", style);
GUILayout.BeginHorizontal();
GUILayout.Label("unity editor resources文件:", GUILayout.Width(160));
EditorGUILayout.TextField(unityEditorResourcesPath, GUILayout.Width(450));
if (GUILayout.Button(EditorGUIUtility.IconContent("d_OpenedFolder Icon"), EditorStyles.miniButtonMid, GUILayout.Width(40)))
{
unityEditorResourcesPath = EditorUtility.OpenFilePanel("请选择Unity安装目录的Editor/Data/Resources/unity editor resources文件", unityEditorResourcesPath, "");
}
if (string.IsNullOrEmpty(unityEditorResourcesPath) == false)
{
if (GUILayout.Button("生成", GUILayout.Width(80)))
{
CreateBuiltInIconList();
}
}
GUILayout.EndHorizontal();
GUILayout.EndVertical();
if (builtInIconListExist < 0)
{
CheckBuiltInIconList();
}
if (builtInIconListExist == 0)
{
GUILayout.Label("请先选择unity editor resources文件路径,并生成");
return;
}
if (iconContentList == null)
{
GetAllIcons();
}
GUILayout.BeginHorizontal();
GUILayout.Label("搜索:", GUILayout.Width(60));
EditorGUI.BeginChangeCheck();
iconFilterStr = EditorGUILayout.TextField(iconFilterStr, EditorStyles.toolbarSearchField, GUILayout.Width(300));
if (GUILayout.Button("刷新", GUILayout.Width(80)))
{
GetAllIcons();
}
if (showIconContentList != null)
{
GUILayout.Label("找到:" + showIconContentList.Count + "个");
}
GUILayout.EndHorizontal();
if (EditorGUI.EndChangeCheck())
{
FilterIconList();
}
if (showIconContentList == null || showIconContentList.Count == 0)
{
GUILayout.Label("没有找到图标");
return;
}
iconScrollPosition = GUILayout.BeginScrollView(iconScrollPosition);
GUILayout.BeginHorizontal();
for (int i = 0; i < showIconContentList.Count; i++)
{
if (GUILayout.Button(showIconContentList[i], GUILayout.Width(iconSize), GUILayout.Height(iconSize)))
{
string iconName = showIconContentList[i].image.name;
CopyValue(iconName);
Debug.Log(iconName + " 已复制到剪切板");
}
if (i != 0 && (i + 1) % rowCount == 0)
{
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
}
}
GUILayout.EndHorizontal();
GUILayout.EndScrollView();
}
/// <summary>
/// 通过搜索过滤内置icon的显示列表
/// </summary>
private void FilterIconList()
{
showIconContentList = new List<GUIContent>();
if (iconContentList != null && iconContentList.Count > 0)
{
string tempStr = iconFilterStr.ToLower();
bool needAdd = false;
for (int i = 0; i < iconContentList.Count; i++)
{
if (string.IsNullOrEmpty(tempStr))
{
needAdd = true;
}
else
{
if (iconContentList[i].image.name.ToLower().Contains(tempStr))
{
needAdd = true;
}
else
{
needAdd = false;
}
}
if (needAdd)
{
showIconContentList.Add(iconContentList[i]);
}
}
}
}
/// <summary>
/// 检查内置icon名字列表是否已经生成
/// </summary>
private void CheckBuiltInIconList()
{
string path = GetIconListPath();
if (System.IO.File.Exists(path))
{
builtInIconListExist = 1;
}
else
{
builtInIconListExist = 0;
}
}
/// <summary>
/// 通过unity editor resource生成内置icon名字列表文件
/// </summary>
private void CreateBuiltInIconList()
{
if(string.IsNullOrEmpty(unityEditorResourcesPath))
{
return;
}
string content = ReadFileText(unityEditorResourcesPath);
if (string.IsNullOrEmpty(content))
{
Debug.LogError("读取失败:"+unityEditorResourcesPath);
builtInIconListExist = 0;
return;
}
List<string> iconList = new List<string>();
string regexStr = @"(?<=icons/)[^\.]+(?=.png)";
string[] matches = GetAllMatchs(content, regexStr);
if (matches != null && matches.Length > 0)
{
string tempStr;
for (int i = 0; i < matches.Length; i++)
{
tempStr = matches[i].Trim();
if (iconList.IndexOf(tempStr) < 0)
{
iconList.Add(tempStr);
}
}
}
regexStr = @"(?<=icons/processed/)[^\.]+(?=.asset)";
matches = GetAllMatchs(content, regexStr);
if (matches != null && matches.Length > 0)
{
string tempStr;
for (int i = 0; i < matches.Length; i++)
{
tempStr = matches[i].Trim();
if (iconList.IndexOf(tempStr) < 0)
{
iconList.Add(tempStr);
}
}
}
string saveContent = "";
if (iconList.Count > 0)
{
for (int i = 0; i < iconList.Count; i++)
{
saveContent += iconList[i];
if (i < iconList.Count - 1)
{
saveContent += "\n";
}
}
}
string savePath = GetIconListPath();
CheckFileSavePath(savePath);
System.IO.File.WriteAllText(savePath, saveContent, System.Text.Encoding.Default);
builtInIconListExist = 1;
GetAllIcons();
}
/// <summary>
/// 从内置icon名字列表读取所有可用的icon
/// </summary>
private void GetAllIcons()
{
iconContentList = new List<GUIContent>();
string path = GetIconListPath();
string content = ReadFileText(path);
if (string.IsNullOrEmpty(content))
{
return;
}
string[] lines = content.Split('\n');
for (int i = 0; i < lines.Length; i++)
{
GUIContent icon = EditorGUIUtility.IconContent(lines[i].Trim());
if (icon != null && icon.image != null)
{
iconContentList.Add(icon);
}
}
FilterIconList();
}
/// <summary>
/// 获取内置icon名字列表的文件路径
/// </summary>
/// <returns></returns>
private string GetIconListPath()
{
return Application.dataPath + "/Editor/builtinIconList.txt";
}
#endregion
#region style
/// <summary>
/// 绘制风格列表
/// </summary>
private void DrawStyle()
{
ShowLine("内置Style");
if (guiStyles == null)
{
GetAllEditorStyles();
}
GUILayout.BeginHorizontal();
GUILayout.Label("搜索:", GUILayout.Width(60));
EditorGUI.BeginChangeCheck();
styleFilterStr = EditorGUILayout.TextField(styleFilterStr, EditorStyles.toolbarSearchField, GUILayout.Width(300));
if (GUILayout.Button("刷新", GUILayout.Width(80)))
{
GetAllEditorStyles();
}
GUILayout.EndHorizontal();
if (EditorGUI.EndChangeCheck())
{
FilterStyleList();
}
if (showGuiStyleList == null || showGuiStyleList.Count == 0)
{
GUILayout.Label("没有找到Style");
return;
}
stylesScrollPosition = GUILayout.BeginScrollView(stylesScrollPosition);
for (int i = 0; i < showGuiStyleList.Count; i++)
{
GUILayout.BeginHorizontal();
EditorGUILayout.TextField(showGuiStyleList[i].name, GUILayout.Width(200));
GUILayout.Label("样例:", GUILayout.Width(80));
GUILayout.Label("这是一个测试的范例", showGuiStyleList[i], GUILayout.Width(400));
GUILayout.EndHorizontal();
if (i < showGuiStyleList.Count - 1)
{
ShowLine("", 750);
}
}
GUILayout.EndScrollView();
}
/// <summary>
/// 通过搜索过滤风格列表
/// </summary>
private void FilterStyleList()
{
showGuiStyleList = new List<GUIStyle>();
if (guiStyles != null && guiStyles.Count > 0)
{
string tempStr = styleFilterStr.ToLower();
bool needAdd = false;
for (int i = 0; i < guiStyles.Count; i++)
{
if (string.IsNullOrEmpty(tempStr))
{
needAdd = true;
}
else
{
if (guiStyles[i].name.ToLower().Contains(tempStr))
{
needAdd = true;
}
else
{
needAdd = false;
}
}
if (needAdd)
{
showGuiStyleList.Add(guiStyles[i]);
}
}
}
}
/// <summary>
/// 获取所有内置的风格
/// </summary>
private void GetAllEditorStyles()