用于UISystem的工具集

简介:上篇文章用于管理Unity中UGUI的工具系统UISystem-优快云博客讲了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内的任何接口架构
以下以此类推

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值