Unity 制作UGUI绑定控件编辑器扩展

在以往的开发中,尤其是一些初学者在书写UI脚本的时候,比如说脚本中需要获取游戏场景中的UI控件,大家都会习惯性的在脚本中定义一个公开变量(也就是public),然后将脚本挂载在物体上,那么就可以直接将控件拖到变量上了。其实像这种拖拽的做法,很多公司商业开发中也是使用拖拽,也不能说拖拽就不好,只是这种自带的拖拽,他需要你的脚本继承monobehaivour才能挂载在物体上,那么很多公司的UI部分都会使用lua脚本进行开发,那么就无法使用这种自带的拖拽方式了,但是一般公司都会自己封装一套拖拽扩展的。

那么我个人是比较习惯使用代码来进行绑定的,因为这样可以省去拖拽的操作,控件一多的话我个人是不太喜欢的,一直拖来拖去。

所以我们今天就来一起实现一个编辑器扩展,让我们可以不需要去定义公开变量以及不用在拖拽控件。

图片

比如说我们现在要制作一个登录面板,那我们需要获取到这个登录面板下的登录按钮对吧?

那么有两种方法,第一种呢是在脚本中定义一个公开变量。然后将其拖进去

图片
在这里插入图片描述

图片那么第二种方法,则是通过路径去查找,使用transform.Find方法,参数是路径。

在这里插入图片描述

使用这种方法,就不需要去拖拽了,我们的变量也不需要公开。

所以我们今天要做的就是实现一个编辑器扩展,可以自动的去生成这些定义变量以及查找变量路径,并根据路径去获取相应控件的代码。这样一来,我们就再也不用去拖拽控件以及写这些重复性的代码了。

所有编辑器扩展的脚本都要放在Editor文件夹下,没有自行创建。接着,我们为我们这个扩展创建一个脚本,这里我取名叫UICodeGenerator(UI代码生成器)。

图片

先引入UnityEditor命名空间,然后让脚本继承Editor。

接下里开始编写这个脚本了,首先我们要让玩家可以使用,如何使用呢?这里就要介绍一个特性了。

图片

加了这个特性的意思就是,首先括号中有三个参数,第一个是路径,第二个是是否点击前调用,第三个是显示优先级。我们注重第一个参数,路径,MenuItem实际上是菜单选项的意思,我们这个代码的意思就是,在这个路径下给用户提供一个用于调用我们这个UICodeGenerate方法的地方,当用户点击这个路径的这个选项,就会调用咱们的这个方法。

图片

右击Hierarchy视图中的物体,就会看到我们刚才定义的这个菜单选项了,所以我们现在的工作就是要完善我们的UICodeGenerate方法。

我们的大体逻辑呢,是去遍历该物体下的所有子物体,然后保存路径,根据路径按照我们的规则去生成代码,然后这些代码,我们会将他复制到剪切板,由用户自行粘贴。为什么不直接生成脚本呢?这个我们最后再来回答。

图片

首先,获取用户选中的物体。然后content是用来保存我们生成的代码的,我们先把所有的代码都加进这个content,最后将content的内容复制到剪切板即可。

至于上面的判断,这是我根据自己的项目做的判断,我要求选中的物体名称必须以Panel结尾,这个可以去掉。个人风格而已

图片

看到这个ctrlsType字典,看看AddCtrlType写的是什么。

图片

这里存储的是你需要查找的控件类型,之所以抽出来这么写是为了方便扩展和修改,可以随意增加删除。

图片

这个ctrlsDic是用来保存找到了的控件的路径和控件类型的。

图片

接下来就是主要逻辑了。其实也不难,就是去遍历刚才我们所写的所需要查找的控件类型,然后使用GetComponentsInChildren获取该物体所有该类型的子控件。这里有一个判断,判断该控件名称是否以prefix开头。

图片

只有以这个开头加下划线的控件才会进行绑定,这个也是可修改的,所以单独列举出来。目的是筛选掉那些不需要绑定的控件,比如说背景图等等。然后去遍历这个子控件,并对各个子控件进行一个while循环,当父物体名称不等于我们选中的物体的名称才继续循环。意思就是当父物体的名称和我们选中的物体的名称相同,就证明不需要继续循环了,因为我们的路径是以该物体为根节点的,我们只需要一直遍历,然后用斜杠把各个物体的名称衔接起来就是路径了,然后将路径和控件类型保存到刚才的ctrlsDic字典中即可。

图片

往我们需要生成的代码中添加一行代码,控件类型,然后是名称,然后换行。

就相当于是Button btn;也就是定义控件的代码。

图片

完整的看一下name这个变量。这里做了一个操作,就是将名称中的Auto换成了我们ctrlsType中该控件类型对应的前缀。也就是说,如果有一个按钮叫Auto_login,那么我们的name就是btn_lgoin。

我们定义变量的部分就结束了,然后我们还要继续写查找控件部分的代码。

图片

首先是先给一下注释,并且给下region特性,这个不懂的可以百度下,就是将该部分查找代码可进行折叠。我们将查找的代码都写在一个AutoBindController方法中,用户直接在Start 或者Awake中调用一下即可。

图片

实际上代码逻辑真的都非常简单,上面已经保存了路径了,我们只需要将路径获取出来,然后根据Find这个方法的书写规范来书写字符串就可以了,说白了都是一些切割字符串的方法,这里看不懂的也只能去自行百度了。

图片

最后需要引入这个类

图片

这个类是需要引入System.Text命名空间的,创建一个TextEditor对象,将content的内容赋值给他,然后调用SelectAll也就是选中所有字符串内容,然后调用Copy,就是复制到剪切板了。然后给下提示。

演示一下,比如我现在创建了一个登录面板预制体。

图片

那么我去创建一个登录面板脚本

图片

引入UI命名空间,接着右键面板预制体

图片

选择CodeGenerate选项。

图片

出现这个提示,应该是没问题了。

然后前往脚本。点击粘贴。

图片

图片

折叠一下并在Awake方法中调用一下就可以了。省去了拖拽的操作,将路径交给系统自己书写,避免了自己书写路径出现的错误。

最后说一下刚才留下的问题,为什么不直接生成脚本,而是将代码复制到剪切板中呢?原因是

如果说我们做登录面板做了登录和注册两个功能,然后策划说要增加一个忘记密码的按钮,那怎么办呢?如果我们这里写了直接生成代码文件,会导致我们已经书写的逻辑被覆盖掉,那么我们就必须先保存现有的逻辑,然后进行替换比较麻烦。当然了,其实可以避免,就是使用partical关键字,可以把一个类分成两个脚本进行书写,将空间绑定的脚本分离开,但是这样会导致脚本增多。还有一个原因就是生成cs文件的话需要动态编译,写起生成代码的逻辑不像上面这么简单,而我们复制到剪切板可以直接将代码都写成字符串,而且也不用担心新增控件的情况,只要删掉之前的,然后粘贴新生成的代码即可。

最后附上完整代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using UnityEngine.UI;
using System;
using System.Text;

/// <summary>
/// 自动生成UI控件绑定代码
/// </summary>
public class UICodeGenerator:Editor
{
    /// <summary>
    /// 需要被绑定的UI控件名称中所需前缀,可修改
    /// </summary>
    static string prefix = "Auto";

    /// <summary>
    /// 添加需要绑定的控件类型和对应的控件名前缀,可修改
    /// 需要被绑定的控件需上面的字符串开头,生成的控件名则自动将上面的前缀转换为下面对应的类型前缀
    /// 示例:控件名Auto_login   代码中则是:btn_login
    /// </summary>
    /// <returns></returns>
    static Dictionary<Type, string> AddCtrlType()
    {
        Dictionary<Type, string> dic = new Dictionary<Type, string>();
        dic.Add(typeof(Button), "btn");
        dic.Add(typeof(Image), "img");
        dic.Add(typeof(RawImage), "rimg");
        dic.Add(typeof(Toggle), "tog");
        dic.Add(typeof(InputField), "input");
        dic.Add(typeof(Text), "txt");
        dic.Add(typeof(Slider), "slr");
        dic.Add(typeof(ScrollRect), "srlRect");

        return dic;
    }

    /// <summary>
    /// 生成代码方法,请勿修改,UI面板名称需以Panel结尾
    /// 在Hirerarchy视图中右键需要生成绑定控件的面板预制体,再选择CodeGenerate,代码则会自动复制到剪切板
    /// </summary>
    [MenuItem("GameObject/CodeGenerate",false,1)]
    static void UICodeGenerate()
    {
        if (Selection.gameObjects.Length < 1)
            return;
        GameObject gameObject = Selection.gameObjects.First();
        if (!gameObject.name.EndsWith("Panel"))
            return;
        StringBuilder content = new StringBuilder();
        Dictionary<Type, string> ctrlsType = AddCtrlType();
        Dictionary<string, Type> ctrlsDic = new Dictionary<string, Type>();
        foreach(Type type in ctrlsType.Keys)
        {
            Component[] components = gameObject.transform.GetComponentsInChildren(type);
            if (components.Length < 1)
                continue;
            for(int i = 0; i < components.Length; i++)
            {
                if (!components[i].transform.name.StartsWith(prefix))
                    continue;
                string name = ctrlsType[type] + components[i].transform.name.Substring(prefix.Length, components[i].transform.name.Length- prefix.Length);
                string path = components[i].transform.name;
                Transform transform = components[i].transform;
                while (!transform.parent.name.Equals(gameObject.name))
                {
                    path = transform.parent.name + "/" + path;
                    transform = transform.parent;
                }
                ctrlsDic.Add(ctrlsType[type] + path, type);
                
                content.Append(type.Name + "\t" + name + ";");
                content.Append("\n");
            }
        }

        content.Append("\n");
        content.Append("#region\t绑定UI控件\t请在面板的初始化方法中调用该方法");
        content.Append("\n");
        content.Append("void\tAutoBindController()");
        content.Append("\n");
        content.Append("{");
        content.Append("\n");
        foreach(var path in ctrlsDic.Keys)
        {
            string prefixTemp = ctrlsType[ctrlsDic[path]];
            string realPath = path.Substring(prefixTemp.Length, path.Length - prefixTemp.Length);
            string nameTemp = realPath.Split('/')[realPath.Split('/').Length - 1];
            string name = prefixTemp + nameTemp.Substring(prefix.Length, nameTemp.Length - prefix.Length);
            content.Append("\t" + name + "\t=\t" + "transform.Find(\"" + realPath + "\").GetComponent<" + ctrlsDic[path].Name + ">();");
            content.Append("\n");
        }
        content.Append("\t}");
        content.Append("\n");
        content.Append("#endregion");

        TextEditor text = new TextEditor();
        text.text = content.ToString();
        text.SelectAll();
        text.Copy();
        Debugger.Log(">>>>>>>>>>>>>>复制成功,请前往UI视图层脚本粘贴代码<<<<<<<<<<<<<<<<<<");
    }

}

代码都在这里了。可以自行尝试。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity UGUI(用户界面)提供了各种控件来创建用户界面。以下是一些常见控件及其事件: 1. Button:Button 控件用于创建一个可点击的按钮。它有以下几个常见的事件: - On Click:当按钮被点击时触发的事件。 - On Pointer Down:当鼠标或手指按下按钮时触发的事件。 - On Pointer Up:当鼠标或手指抬起按钮时触发的事件。 - On Pointer Enter:当鼠标或手指进入按钮区域时触发的事件。 - On Pointer Exit:当鼠标或手指离开按钮区域时触发的事件。 2. Toggle:Toggle 控件用于创建一个可切换状态的开关。它有以下几个常见的事件: - On Value Changed:当 Toggle 的值(选中或未选中)发生改变时触发的事件。 3. Slider:Slider 控件用于创建一个可以拖动的滑块。它有以下几个常见的事件: - On Value Changed:当 Slider 的值发生改变时触发的事件。 4. Scrollbar:Scrollbar 控件用于创建一个可以拖动的滚动条。它有以下几个常见的事件: - On Value Changed:当 Scrollbar 的值发生改变时触发的事件。 5. Dropdown:Dropdown 控件用于创建一个下拉框。它有以下几个常见的事件: - On Value Changed:当 Dropdown 的值(选中的选项)发生改变时触发的事件。 6. InputField:InputField 控件用于创建一个可以输入文本的框。它有以下几个常见的事件: - On Value Changed:当 InputField 的值发生改变时触发的事件。 - On End Edit:当用户完成输入并按下回车键或点击其他地方时触发的事件。 除了以上列出的常见事件之外,每个控件还可能有其他的事件。可以通过在 Inspector 窗口中选择相应的控件并查看其属性面板来了解更多信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值