简介:上篇文章用于管理Unity中UGUI的工具系统UISystem-CSDN博客讲了UISystem,为了更加方便使用,我给他写了一个编辑器工具,下面展示代码和使用说明,具体详情不难看一下就看懂了。
一、代码部分
using QFramework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using Unity.EditorCoroutines.Editor;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
public class 快速创建UI物体及绑定脚本 : EditorWindow
{
private string objectName;
private string UIScriptPathName = "Assets/Scripts";
private string UIInScenePathName="Canvas";
private string UIPrefabPathName= "Assets/Resources/UiPanel";
private GameObject newObject;
private Type scriptType;
private string scriptFullName;
private bool isWaitingForNextFrame = false;
// 添加一个布尔变量来存储勾选框的状态
private bool isChecked;
// 添加一个布尔变量来存储basepanel的状态
private bool isBasePanel;
// 控制显示UI元素的变量
private bool showPrefabOptions = false;
private bool[] SelectObjectType = new bool[4]; // 3个选项,Image, Button, Text
[MenuItem("MyEditor/UIKit")]
public static void OpenWindow()
{
var window = (快速创建UI物体及绑定脚本)GetWindow(typeof(快速创建UI物体及绑定脚本), true);
window.position = new Rect(500, 300, 600, 400);
window.Show();
}
public void OnGUI()
{
#region UI名称
// 开始一个水平布局
GUILayout.BeginHorizontal();
// 添加提示文字
GUILayout.Label("输入需要创建UI名称:", GUILayout.Width(150));
// 添加输入框,允许用户输入物体名称
objectName = GUILayout.TextField(objectName, GUILayout.Width(300));
// 结束水平布局
GUILayout.EndHorizontal();
#endregion
#region UI脚本路径
// 开始一个水平布局
GUILayout.BeginHorizontal();
// 添加提示文字
GUILayout.Label("输入创建UI脚本路径:", GUILayout.Width(150));
// 添加输入框,允许用户输入物体名称
UIScriptPathName = GUILayout.TextField(UIScriptPathName, GUILayout.Width(300));
// 结束水平布局
GUILayout.EndHorizontal();
#endregion
#region UI场景中位置
// 开始一个水平布局
GUILayout.BeginHorizontal();
// 添加提示文字
GUILayout.Label("输入创建UI在场景中的位置:", GUILayout.Width(150));
// 添加输入框,允许用户输入物体名称
UIInScenePathName = GUILayout.TextField(UIInScenePathName, GUILayout.Width(300));
// 结束水平布局
GUILayout.EndHorizontal();
#endregion
#region 是否生成预制体勾选框
// 在界面上方添加一个勾选框
// 使用 Toggle 获取勾选框的状态,并根据状态执行不同的方法
isChecked = EditorGUILayout.Toggle("是否生成预制体", isChecked); // 将勾选框的状态赋值给isChecked
if (isChecked) // 判断勾选框的状态
{
#region UI预制体生成路径
GUILayout.BeginHorizontal();
GUILayout.Label("输入UI预制体生成路径:", GUILayout.Width(150));
UIPrefabPathName = GUILayout.TextField(UIPrefabPathName, GUILayout.Width(300));
GUILayout.EndHorizontal();
#endregion
//生成预制体
}
#endregion
#region 创建物体类型
// 使用 GUIContent 来处理文字换行,避免文字超出
GUILayout.BeginHorizontal();
GUIContent labelContent = new GUIContent("选择创建的UI类型(默认为GameObject)");
GUILayout.Label(labelContent, GUILayout.Width(220)); // 设置一个合适的宽度
// 控制显示是否创建UI类型的 Toggle
showPrefabOptions = EditorGUILayout.Toggle("", showPrefabOptions); // 可以设置为一个适合的宽度
GUILayout.EndHorizontal(); // 结束水平布局
if (showPrefabOptions)
{
// 使用Toggle模拟单选,确保只能勾选一个
for (int i = 0; i < SelectObjectType.Length; i++)
{
bool isSelected = GUILayout.Toggle(SelectObjectType[i], GetObjectTypeLabel(i));
if (isSelected && !SelectObjectType[i])
{
// 如果当前项被选中,则取消其他项的选中状态
for (int j = 0; j < SelectObjectType.Length; j++)
{
SelectObjectType[j] = (i == j); // 确保只有当前项被选中
}
}
}
}
#endregion
#region 是否继承BasePanel
GUILayout.BeginHorizontal();
GUIContent labelContent2 = new GUIContent("是否继承BasePanel(总Panel必须继承)");
GUILayout.Label(labelContent2, GUILayout.Width(220)); // 设置一个合适的宽度
isBasePanel = EditorGUILayout.Toggle("", isBasePanel); // 将勾选框的状态赋值给isBasePanel
GUILayout.EndHorizontal(); // 结束水平布局
#endregion
#region 创建按钮
if (GUILayout.Button("Create UI Object"))
{
CreateUIObject();
}
#endregion
if (isWaitingForNextFrame)
{
OnNextFrame();
isWaitingForNextFrame = false;
}
}
private string GetObjectTypeLabel(int index)
{
switch (index)
{
case 0: return "Image";
case 1: return "Button";
case 2: return "Text";
case 3: return "总Panel";
default: return "";
}
}
private void CreateUIObject()
{
// 创建一个空物体
newObject = new GameObject(objectName);
// 找到父物体,如果UIPrefabPathName指向的是场景中已有物体的名字
GameObject parentObject = GameObject.Find(UIInScenePathName);
// 如果找到了父物体,则将新物体设置为父物体的子物体
if (parentObject != null)
{
newObject.transform.SetParent(parentObject.transform);
}
else
{
// 如果没有找到父物体,可以选择将它添加到场景根目录,或者根据需求处理
Debug.LogError("指定的父物体不存在: " + UIInScenePathName);
}
// 获取物体的名字
objectName = newObject.name;
if (showPrefabOptions)//确保勾选了要创建ui类型的物体在遍历
{
for (int i = 0; i < SelectObjectType.Length; i++)
{
if (SelectObjectType[i])//不是物体而是ui类型 && (i != SelectObjectType.Length - 1)
{
RectTransform rectTransform = newObject.AddComponent<RectTransform>();
switch (i)
{
case 0: CreateUI_ImageTypeSet(); break;
case 1: CreateUI_ButtonTypeSet(); break;
case 2: CreateUI_TextTypeSet(); break;
case 3: CreateUI_BasePanelTypeSet(); break;
}
//这里可以添加每个类型默认的设置,比如字体,文本,颜色等
}
}
}
// 生成脚本的路径
string scriptPath = UIScriptPathName +"/"+ objectName + ".cs"; //$"PathName/{objectName}.cs";
Debug.Log(scriptPath);
// 检查脚本是否已经存在
if (!File.Exists(scriptPath))
{
// 创建一个新的脚本
string scriptTemplate = CreateNewScript();
// 写入脚本内容
File.WriteAllText(scriptPath, scriptTemplate);
// 刷新文件夹
AssetDatabase.Refresh();
// 监听脚本编译完成事件
CompilationPipeline.assemblyCompilationFinished += OnAssemblyCompilationFinished;
}
else
{
Debug.LogError("脚本已经存在: " + scriptPath);
}
}
// 监听编译完成事件
private void OnAssemblyCompilationFinished(string assemblyPath, CompilerMessage[] compilerMessages)
{
Debug.Log("脚本编译完成了");
// 检查编译的是否是目标脚本
if (assemblyPath.Contains("Assembly-CSharp"))
{
scriptFullName = objectName + ", Assembly-CSharp"; // 确保使用完整类型名称
GameObject newObject = GameObject.Find(UIInScenePathName); // 获取创建的物体
scriptType = null;
Debug.Log("下一帧");
OnNextFrame();
// 取消注册监听
CompilationPipeline.assemblyCompilationFinished -= OnAssemblyCompilationFinished;
}
else
{
Debug.LogError("脚本编译没有完成");
}
}
private void OnNextFrame()
{
Debug.Log("进入下一帧");
scriptType = Type.GetType(scriptFullName);
if (scriptType == null)
{
Debug.Log("空");
isWaitingForNextFrame = true;
Repaint(); // 如果脚本没加载,再次请求重绘
}
else
{
Debug.Log(scriptType);
// 脚本加载完成,添加组件
newObject.AddComponent(scriptType);
if (isChecked)
{
// 检查UIPrefabPathName是否为空,避免路径为空的情况
if (string.IsNullOrEmpty(UIPrefabPathName))
{
Debug.LogError("预制体保存路径不能为空!");
return;
}
// 创建预制体
string prefabPath = Path.Combine(UIPrefabPathName, objectName + ".prefab");
// 使用PrefabUtility将物体保存为预制体
PrefabUtility.SaveAsPrefabAsset(newObject, prefabPath);
}
}
}
private void CreateUI_ImageTypeSet()
{
newObject.AddComponent<UnityEngine.UI.Image>();
(newObject.transform as RectTransform).anchoredPosition = Vector3.zero;
}
private void CreateUI_ButtonTypeSet()
{
UnityEngine.UI.Image image = newObject.AddComponent<UnityEngine.UI.Image>();
newObject.AddComponent<UnityEngine.UI.Button>();
(newObject.transform as RectTransform).anchoredPosition = Vector3.zero;
(newObject.transform as RectTransform).sizeDelta = new Vector2(160,30);
}
private void CreateUI_TextTypeSet()
{
UnityEngine.UI.Text text = newObject.AddComponent<UnityEngine.UI.Text>();
text.color = UnityEngine.Color.black;
text.text = "New Text";
(newObject.transform as RectTransform).anchoredPosition = Vector3.zero;
(newObject.transform as RectTransform).sizeDelta = new Vector2(160, 30);
}
private string CreateNewScript()
{
string scriptNewTemplate = "";
if (isBasePanel)
{
scriptNewTemplate = "using UnityEngine;\n\n" +
"namespace QFramework\n" + "{\n" +
"public class " + objectName+ " : BasePanel\n" + "{\n" + CreateNewScript_Supplement() + "}\n" + "}";//
}
else
{
scriptNewTemplate = "using UnityEngine;\n\n" +
"public class " + objectName+" : MonoBehaviour\n"+ "{\n" + "}";
}
return scriptNewTemplate;
}
private string CreateNewScript_Supplement()
{
string ChildForClose = "\"" + objectName + "面板关闭" + "\"";
string ChildForHide = "\"" + objectName + "面板隐藏" + "\"";
string ChildForOpen = "\"" + objectName + "面板打开" + "\"";
string ChildForPause = "\"" + objectName + "面板暂停" + "\"";
string ChildForResume = "\"" + objectName + "面板恢复" + "\"";
string ChildForShowAgain = "\"" + objectName + "面板再次显示" + "\"";
string overrideFunc = "";
overrideFunc += "public override void ChildForOpen()\n" + "{\n" + "Debug.Log(" + ChildForOpen + ")" + ";\n" + "}\n";
overrideFunc += "public override void ChildForResume()\n" + "{\n" + "Debug.Log(" + ChildForResume + ")" + ";\n" + "}\n";
overrideFunc += "public override void ChildForShowAgain()\n" + "{\n" + "Debug.Log(" + ChildForShowAgain + ")" + ";\n" + "}\n";
overrideFunc += "public override void ChildForPause()\n" + "{\n" + "Debug.Log(" + ChildForPause + ")" + ";\n" + "}\n";
overrideFunc += "public override void ChildForHide()\n" + "{\n" + "Debug.Log(" + ChildForHide + ")" + ";\n" + "}\n";
overrideFunc += "public override void ChildForClose()\n" + "{\n" + "Debug.Log(" + ChildForClose + ")" + ";\n" + "}\n";
return overrideFunc;
}
private void CreateUI_BasePanelTypeSet()
{
}
}
有很多不完整的部分,不过不影响使用,比如使用 window.titleContent = new GUIContent("UISystem组装外界碎片UI层级及组件");更改编辑器工具名字而不是类名,再比如有废弃掉的方法string没有删除而是置空等等。
二、使用说明
编辑器说明:
1.输入需要创建UI名称:这里不能为空,为空则退出编辑器创建
2.输入创建UI脚本路径:这里默认目录为Assets/Scripts/UI,可以自己更改,为空则退出编辑器创建,插件可以自动创建目录文件夹
3.输入创建UI在场景中的位置:这里默认目录为Canvas,相对于场景根目录,建议Canvas下一级目录为BasePanel,负责模块化Panel,需要创建BasePanel预制体,为空或场景中为找见该目录则退出编辑器创建
4.是否生成预制体:如果勾选则输入预制体保存路径,否则不需要做改动。如果勾选后路径为空则退出编辑器创建
5.选择创建的UI类型(默认为GameObject):如果不勾选则默认为继承monobehaviour的空类,勾选则显示需要创建的Panel类型,建议Canvas下一级目录选择总Panel,并且继承BasePanel(如果需要使用UISystem)
6.是否继承BasePanel(总Panel必须继承):勾选后继承类为BasePanel,不过BasePanel继承了monobehaviour
7.继承的父物体名称:如需使用则为空或Canvas下一级目录,在一级目录下创建二级、三级等更多目录则自动更新一级目录,建议在一级目录后创建更深层目录填写,并且保证填写名称与输入创建UI在场景中的位置的内容在第一个/与第二个/之间的内容相同,会自动从第二个/前目录在场景中查找并更新预制体与相对路径。对于更多层次的目录,则继承填写的名称,如果按照建议则继承他的根父物体类,父物体继承BasePanel。
操作指南:
一、创建一级BasePanel目录
操作过程:
1.输入需要创建UI名称:SystemPanel
2.输入创建UI脚本路径:Assets/Scripts
3.输入创建UI在场景中的位置:Canvas
4.是否生成预制体:√
输入UI预制体生成路径:Assets/Resources/UiPanel
5.选择创建的UI类型(默认为GameObject):总Panel
6.是否继承BasePanel(总Panel必须继承):√
7.继承的父物体名称:空,这里不需要继承
结果:
得到Canvas下一级目录SystemPanel并且有一个脚本为SystemPanel赋上,在Assets/Resources/UiPanel路径下有SystemPanel预制体,在Assets/Scripts有SystemPanel脚本
二、创建二级目录
操作过程:
1.输入需要创建UI名称:SystemPanelClickFunc
2.输入创建UI脚本路径:Assets/Scripts
3.输入创建UI在场景中的位置:Canvas/SystemPanel
4.是否生成预制体:×
5.选择创建的UI类型(默认为GameObject):Button
6.是否继承BasePanel(总Panel必须继承):×
7.继承的父物体名称:SystemPanel
结果:
得到Canvas下一级目录SystemPanel下的二级目录SystemPanelClickFunc并且有一个脚本为SystemPanelClickFunc赋上,自动更新SystemPanel预制体,在Assets/Scripts有SystemPanelClickFunc脚本,SystemPanelClickFunc可以自己继承UISystem以及QFrameWork内的任何接口架构
三、创建三级目录
操作过程:
1.输入需要创建UI名称:SystemPanelThreeFunc
2.输入创建UI脚本路径:Assets/Scripts
3.输入创建UI在场景中的位置:Canvas/SystemPanel/SystemPanelClickFunc
4.是否生成预制体:×
5.选择创建的UI类型(默认为GameObject):Button
6.是否继承BasePanel(总Panel必须继承):×
7.继承的父物体名称:SystemPanel
结果:
得到Canvas下一级目录SystemPanel下的二级目录SystemPanelClickFunc下的三级目录SystemPanelThreeFunc并且有一个脚本为SystemPanelThreeFunc赋上,自动更新SystemPanel预制体,在Assets/Scripts有SystemPanelThreeFunc脚本,SystemPanelThreeFunc可以自己继承UISystem以及QFrameWork内的任何接口架构
以下以此类推