Unity编辑器扩展 UI控件篇

Posted 心之凌儿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity编辑器扩展 UI控件篇相关的知识,希望对你有一定的参考价值。

前摇 :认识编辑器扩展的必要性

由于各种各样的原因,无论是移动端亦或是主机/PC端,进几年的发行的游戏体量是越来越大。通常来说大体量的游戏开发需要一套很成熟完善的工作流,亦或说有很强的工业化的能力,像R星育碧这样的游戏厂商一定会有非常强大的工业化能力,才能有大表哥这样充斥大量细节或刺客信条这样充斥BUG,口误,是持续迭代的游戏作品

所谓工欲善其事必先利其器,要想提升游戏开发效率,就要有好用的游戏开发工具,说到最后就绕不开游戏开发引擎

游戏业界常说,自研的引擎不一定是最好的,但是最好的引擎一定是自研的,一定程度上说明了定制化引擎存在优势。 虽然像Unity与虚幻这样的商业引擎已经非常成熟好用,但其本身还是通用性的商品,很难在每一个方面都做到很好的适配性

而就Unity引擎,又相比于虚幻来说在整个工作流上表现还不够成熟。操作方式使用习惯上还是存在非常多的程序员思想,很多开发工具可视化程度还不够完善,对于程序员来说也许问题不大,但是对策划与美术来说就非常不友好

各种层面上来说,为提升编辑器的易操作性与保障项目开发独特性,而对编辑器进行扩展魔改,已经变成了游戏开发者所必须的技能之一

发生:Unity编辑器扩展之UI控件

乱七八糟的话说完后,回归正题。根据Unity文档的解释与案例,了解编辑器界面创建过程所需的结构与方法

与做游戏UI界面不同,引擎编辑器界面的搭建完全没有现成控件做可视化搭建,而只能依靠较底层的代码命令手撸完成。不过好在Unity提供了一套完整的纯代码界面编辑模式,并称其为“即时模式”GUI 系统。关于该系统的具体解释这里直接搬运官方文档的说明:

  • “即时模式”GUI 系统(也称为 IMGUI)是一个完全独立的功能系统,不同于 Unity 基于游戏对象的主 UI 系统。IMGUI 是一个代码驱动的 GUI 系统,主要用作程序员的工具

虽然该系统与主UI系统不太相同,但是也同根同源,学习起来难度并不大。通过下面的一个小Demo来演示IMGUI的具体使用方法与应该注意的一些点

继承EditorWindow创建编辑器窗口:

编辑器UI界面展示与逻辑刷新一般都是继承于EditorWindow这个类来实现的,对其生命周期结构的使用类似于通过编辑继承MonoBehavior的脚本代码完成游戏生命周期逻辑的实现。两者有着相似的监听事件,一样的周期性刷新方法,所以可以无学习的成功的上手使用

Unity中创建并继承于EditorWindow类编辑器脚本并命名为EditorDemo ,然后使用类中的GetWindow方法创建CreateWindow静态方法(注意一定要是静态),通过该静态方法实现编辑器窗口界面的初始化,具体的代码细节如下:

public class EditorDemo : EditorWindow

    [MenuItem("EditorDemo/Test  &1")]
    public static EditorDemo CreateWindow()
    
        EditorDemo window = GetWindow<EditorDemo>("测试编辑器");
        window.autoRepaintOnSceneChange = true;
        window.Show();
        return window;
    

在上面的代码中,会在初始化静态方法上添加MenuItem命令用以实现在引擎内的上部导航栏中增加一个自定义编辑器的开启页签

  • 关于MenuItem:
    MenuItem 属性用于向主菜单和检视面板上下文菜单添加菜单项,使用特定格式可以实现一些额外效果,如上面案例中使用反斜杠符号可以创建多个层级的菜单,而“&”符号则可以为该编辑器界面添加开启快捷键(通常与Alt形成组合键),如图所示:

在对编辑器界面完成初始化的工作后,后面的界面细节需要对EditorWindow类做一些了解。查阅文档来熟悉以下该类提供的一些消息函数。了解他们分别用来实现不同状态下的哪些编辑器功能:

在上面的消息,对编辑器扩展UI方面最需要关注的OnGUI函数,通常来说,界面元素的创建通常都需要写入到改方法内。前面有说到即时模式GUI创建编辑器UI控件无法通过可视化的操作工具来完成,只能由Unity提供的相关API来做,过程类似于不使用游戏开发引擎,直接基于相应编程语言的游戏开发,常用的控件创建类有下面几个:

  • GUI:是UnityEngine下的类,其方法可在Runtime状态下调用执行
  • GUILayoutGUI的可自动排版版本,无需Rect布局定位
  • EditorGUI:是UnityEditor下的编辑器类
  • EditorGUILayoutEditorGUI的可自动排版版本,无需Rect布局定位

通过四个类的命名也很容易的看出他们之间的区别与使用区间。通常来说,GUIGUILayout是使用最广的,编辑器状态与RunTime下都可用。独立的编辑窗口通常GUI与EditorGUI两个类可以混合使用,而在运行状态下使用就只能使用GUIGUIlayerout

以使用场景较多的GUIlayout为案例,可以从文档介绍中看到Unity引擎提供了相当多的创建控件的接口,熟悉使用这些接口,完全可以利用自己游戏界面的开发界面方便的开发出所需的编辑器


通过一个简单的案例介绍这些API的使用方法。尝试在场景中选中某一个物体,并通过编辑器来控制其整体缩放与颜色改变:

首先关于选择对象的获取,Unity提供了获取编辑器状态下的开发者所选择的对象编辑器方法Selection.activeGameObject,而在前面的关于EditorWindow的消息中存在OnSelectionChange函数,该函数会在选择对象改变时被调用。通过使用这两个函数与接口可以高效的得到实时选择的物体:

    public GameObject selectObj;
    private void OnSelectionChange()
    
        selectObj = Selection.activeGameObject;
    

在得到编辑器场景内的某一物体后,通常我们可以调节Inspector面板内的对应参数来改变选中对象的状态。下面的代码是编辑器控件来模仿这个效果:

    public string selectName;
	public string textStr;
    public float slideNum;
    public Color color;
    private void OnGUI()
    
        selectName = selectObj == null ? "未选中物体" : selectObj.name;
        selectName = EditorGUILayout.TextField("选中的物体名:", selectName);
        GUILayout.Space(20);
        slideNum = EditorGUILayout.Slider("控制对象缩放:", slideNum, 1, 10);
        if (selectObj!=null)
        
            selectObj.transform.localScale=new Vector3(slideNum,slideNum,slideNum);
        
        GUILayout.Space(20);
        color = EditorGUILayout.ColorField("选择颜色:",color);
        GUILayout.Space(10);
        if (GUILayout.Button("点击更换颜色")&&selectObj != null)
        
            selectObj.GetComponent<MeshRenderer>().material.color = color;
        
    

演示效果:

Inspector面板之脚本组件扩展:

有时某些扩展需求不需要通过独立窗口来完成,比如对某些脚本组件希望有一些轻量的编辑操作,就可以做一些嵌入式的方法扩展

简单的做一个案例演示。首先创建一个继承于MonoBehavior脚本并挂载到场景中的物体上,然后在该脚本中写入一个对字符串填充数据的方法:

public class Demo : MonoBehaviour

    public string  str;
    public void ChangeStrDemo()
    
        str = "编辑器扩展测试";
    

然后创建一个继承于Editor的编辑器扩展类,代码如下:

[CustomEditor(typeof(Demo))]
class EditorInspectorDemo : Editor

    public Demo demo;
    private void Awake()
    
        demo = target as Demo;
    
    public override void OnInspectorGUI()
    
        base.OnInspectorGUI();
        if(GUILayout.Button("测试按钮"))
        
            demo.ChangeStrDemo();
        
    

完成上面的代码后回到引擎中,就可以得到一个编辑器中执行函数的方法

小Tip:

编辑器状态下执行函数方法有很多中方式,如最简单的常用的ContextMenu命令。通过在脚本函数上添加命令,就可以在编辑器类快速调用方法,而且与Runtime模式不同,该方式会保存修改后的状态,非常适合快速开发扩展

Runtime模式下Game窗口扩展:

Runtime模式下可以在MonoBehavior脚本中直调用GUI方法做扩展,通过一个案例来简单的陈述该过程:

通过创建一个游戏内窗口得到事件响应控件(Button),并实现一个编辑器方法:记录打开指定文件目录内的文件的资源路径:

public class Demo : MonoBehaviour

    private void OnGUI()
    
        GUI.Window(908, new Rect(0, 0, 300, 300), CreateGuiWindow, "测试小窗口");
    
    void CreateGuiWindow(int id)
    
        if (GUILayout.Button("打开asset文件夹并筛选文件"))
        
            EditorUtility.OpenFilePanel("选择文件", Application.dataPath,"cs");            
        
        if (GUILayout.Button("打开asset文件夹选择文件夹"))
        
             EditorUtility.OpenFolderPanel("选择文件夹", Application.dataPath, "");
               
    


执行上面的代码,效果如下:

由于Runtime模式下Game窗口扩展通常都是基于自身项目结构的模式执行的,所以用上面的这个案例来理解其使用模式并不是很合适,不过该模式并不会经常使用到,稍微了解其控件用法即可

虽然Runtime模式下编辑器扩展不常用到,但是相比于Editor状态是有更高的自由度,可以执行很多运行状态下才可调起的方法函数,或者说可以更好的和当前项目的框架内容交互。而不用额外再j将其转为编辑器方法,这对一些小的功能扩展来说可能会很高效

UI控件小集合:

具体的UI控件可以通过查阅文档获取,链接地址如下:

为了方便重复查阅,这里也简要罗列一些常用的:

EditorGUILayout:

  • TextArea: 创建一个文本区域

  • TextField: 创建一个文本字段

  • IntField与FloatField:数字输入框

  • Vector2Field与Vector3Field等:向量输入框

  • ObjectField:生成一个可接收任何对象类型的字段

  • Space: 在上一个控件和下一个控件之间留出一个小空间。

  • BoundsField: 创建用于输入 Bounds 的 Center 和 Extents 字段。

  • ColorField:颜色选择器

  • Toggle: 创建一个开关

  • RectField 创建用于输入 Rect 的 X、Y、W 和 H 字段

  • EditorToolbar: 创建一个用指定的编辑器工具集合填充的工具栏

  • EnumFlagsField: 单击后,系统会为枚举类型的每个值显示带有选项的菜单

  • EnumPopup: 创建一个枚举弹出选择字段

  • IntSlider: 创建一个滑动条,用户可以进行拖动以在最小值和最大值之间更改整数值

  • Foldout: 创建一个左侧带有折叠箭头的标签

  • HelpBox: 创建一个带有发送给用户的消息的帮助框

  • InspectorTitlebar: 创建一个类似于 Inspector 窗口的标题栏

  • LayerField: 创建一个层选择字段

  • TagField: 创建一个标签选择字段

  • PasswordField: 创建一个可让用户输入密码的文本字段。

后摇:编辑器扩展功能篇预告

上面介绍了一些基本的编辑器界面搭建相关的UI控件,算是说明了一些基础的知识。而编辑器扩展除了界面外,另外一块比较重要的是编辑器扩展之编辑功能的实现。如上面Runtime案例中的打开本地文件夹的小案例,就是编辑器扩展中编辑功能的体现,通过UI控件维护触发这些提升开发效率的辅助功能才是扩展的核心所在

为了使编辑器扩展的内容介绍更完整,我会努力尽快更新下篇的,当然最重要的是,如果有大佬发现文章中存在的任何问题,希望可以留言指出,我会在后续努力完善的

Unity3D编辑器扩展Unity3D中实现UI界面控制,UI界面的显示和隐藏实现

推荐阅读

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。

一、前言

在开发中,可能遇到管理很多的UI界面,但是需要切换UI界面的情况。

这种情况下,通常就是在Hierarchy视图中勾选需要显示的UI界面,关闭不需要显示的UI界面

这种操作很麻烦,所以就写了一个直接在脚本组件中控制UI界面的功能,可以直接切换UI界面,提升效率。

先来看一下效果图:

二、实现

(1)先搭建UI界面,做两个UI界面进行切换:

(2)新建ChangeModule.cs脚本,双击修改脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

[System.Serializable]
public class ChangeModule

    [SerializeField] private string m_Name;
    [SerializeField] private bool m_Selected;
    [SerializeField] private GameObject m_Panel;

    public string name  get  return m_Name;  set  m_Name = value;  
    public bool select  get  return m_Selected;  set  m_Selected = value;  
    public GameObject panel  get  return m_Panel;  set  m_Panel = value;  

这个脚本主要是对每个UI界面控制的对象的属性设置。

(3)新建ChangePanel.cs脚本,双击修改脚本:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using XCharts;

[DisallowMultipleComponent]
[ExecuteInEditMode]
public class ChangePanel : MonoBehaviour

    [SerializeField] private List<ChangeModule> m_ChangeModule = new List<ChangeModule>();

    void Awake()
    
        InitModuleButton();
    

    void Update()
    
#if UNITY_EDITOR
        InitModuleButton();
#endif
    

    void InitModuleButton()
    
        for (int i = 0; i < m_ChangeModule.Count; i++)
        
            var module = m_ChangeModule[i];
            if (module.select)
            
                module.panel.SetActive(true);
                module.select = true;
            
            else
            
                module.panel.SetActive(false);
                module.select = false;
                
        
    

这个脚本就是为了控制UI界面的。

(4)将ChangePanel.cs脚本组件添加到任意对象,现在就可以在不运行的状态下控制UI界面了:

(5)在上一步其实就已经完成了功能,但是我还想将这个再优化一下,所以就新建一个Editor文件夹,在Editor文件夹里面新建ChangeModuleDrawer.cs脚本,双击修改脚本:

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(ChangeModule), true)]
public class ChangeModuleDrawer : PropertyDrawer

    public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label)
    
        Rect drawRect = pos;
        drawRect.height = EditorGUIUtility.singleLineHeight;
        SerializedProperty m_Name = prop.FindPropertyRelative("m_Name");
        SerializedProperty m_Selected = prop.FindPropertyRelative("m_Selected");
        SerializedProperty m_Panel = prop.FindPropertyRelative("m_Panel");
        drawRect.width = 30;
        EditorGUI.PropertyField(drawRect, m_Selected, GUIContent.none);
        drawRect.x += 30;
        drawRect.width = 100;
        EditorGUI.PropertyField(drawRect, m_Name, GUIContent.none);
        drawRect.x += 120;
        drawRect.width = 200;
        EditorGUI.PropertyField(drawRect, m_Panel, GUIContent.none);
    

    public override float GetPropertyHeight(SerializedProperty prop, GUIContent label)
    
        return 1 * EditorGUIUtility.singleLineHeight + 1 * EditorGUIUtility.standardVerticalSpacing;
    

效果图:

现在感觉方便多了。

三、后记

本篇文章实现了Unity3D的拓展开发,可以快速的切换UI界面,适用于UI界面比较多的情况。


你的点赞就是对博主的支持,有问题记得留言:

博主主页有联系方式。

博主还有跟多宝藏文章等待你的发掘哦:

专栏方向简介
Unity3D开发小游戏小游戏开发教程分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。
Unity3D从入门到进阶入门从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。
Unity3D之UGUIUGUIUnity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据文件读取使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合数据集合数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发虚拟仿真总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件插件主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发日常记录主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG日常记录记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。

以上是关于Unity编辑器扩展 UI控件篇的主要内容,如果未能解决你的问题,请参考以下文章

Unity3D编辑器扩展Unity3D中实现UI界面控制,UI界面的显示和隐藏实现

Unity编辑器扩展——自动生成UI界面脚本

Unity编辑器扩展——自动生成UI界面脚本

Unity编辑器扩展——查看编辑器内置样式GUIStyle

Unity编辑器扩展——查看编辑器内置样式GUIStyle

unity shader 编辑器扩展类 ShaderGUI