Unity 核心系统详解 -- UI 系统

1,574 阅读15分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Unity 提供了三个 UI 系统,可以使用它们在 Unity 编辑器中创建的应用程序创建用户界面 (UI):

  • UI Toolkit
  • Unity UI 软件包 (uGUI)
  • IMGUI Unity 打算让 UI Toolkit 成为新 UI 开发项目的推荐 UI 系统,但它仍然缺少 Unity UI (uGUI) 和 IMGUI 中的一些功能。且目前 UI Toolkit 还处于 preview 版本,所以本文着重介绍最为主流的 uGUI。

1 Canvas

Canvas 在英语里是画布的意思。在 Unity 中,所有的 UI 对象必须作为 Canvas 的子对象存在。游戏场景中的 Canvas 对象关联着 Canvas 组件,是所有 UI 对象的容器。不过,一个场景中并不是只能存在一个 Canvas。用户可以根据设计需求使用多个 Canvas,不用把所有 UI 对象放置于同一个 Canvas 中。

在 Unity 中新建一个项目,并将其命名为 UITest 新建一个场景,保存并命名为 TestScene,在 Hierarchy 视图中的空白处右击,可以看到 Unity 中的 UI 相关对象,如下图所示

如果场景中没有 EventSystem, Unity 还会自动创建一个 EventSystem。简单来说 EventSystem 的作用就是处理场景中 UI 的交互事件。至于 Canvas 组件本身,目前只需要了解了种不同的 Render Mode(渲染模式)。

a. Screen Space-Overlay

如下图所示,Canvas 默认的渲染模式为 Screen Space-Overlay(屏幕空间 - 覆盖),此时 Canvas 下的所有 UI 对象会被直接绘制在游戏窗口的平面上。也就是说,此时 Canvas 中的 UI 对象会优先显示在游戏场景中,不会被其他任何游戏对象所遮挡。

在默认模式下,我们无法手动修改 Canvas 的尺寸等信息,因为 Canvas 是覆盖在整个 Game 视图之上的。它的大小由 Game 视图的大小决定。

b. Screen Space-Camera

第二种渲染模式是 Screen Space-Camera(屏幕空间一摄像机)。在此模式下,Canvas 的显示效果和第一种渲染模式的效果基本一致,只是在 Scene 视图中有较大区别。将 Canvas 默认的谊染模式切换为 Screen Space-Camera 后,需要手动指定 Render Camera, 如下图所示。

此时,Canvas 会在 Camera 前方的一定距离显示,这个距离由 Plane Distance 参数决定

在 Sercen Space- Overlay 和 Sercen Space- Camera 两种模式下,Canvas 的尺寸和 Game 视图的尺寸一致。Canvas 会将所有 UI 对象渲染在屏幕最顶端,不会受场景中其他任何物体的影响。如果屏幕尺寸改变,Canvas 会自动适应屏幕尺寸。

c. World Space

第三种渲染模式是 World Space(世界空间)。需要注意的是,在 VR 项目开发中,所有的 Canvas 都必须设置为 world Space 模式。在此模式下,整个 Canvas 将和其他对象一样作为 3D 对象存在于场景中。更多详细的设置将在后续章节中讲解。

2 Image

UGUI 系统可以使用图片让用户界面更加吸引人。

在 Hierarchy 视图中的空白处右击,选择 UI 一 Image 命令,从而添加一个 Image 对象。此时,系统会自动创建一个 Canvas 对象和一个 Eventsystem 对象,如图所示

选中 Image 对象,可以看到在 Inspector 视图中,它添加默认关联着 4 个组件,如下图所示其中,Rect Transform 组件用于控制 UI 对象的位置、旋转、缩放和锚点 (Anchor) 等属性。

Rect Transform 组件中的位置、旋转、缩放属性和 Transform 组件中对应属性基本一致,而锚点则是个全新的概念。点击 Anchors左侧下拉列表框就可以看到锚点的具体信息

其中,Min 和 Max 默认 X/Y 值都是 0.5,此时锚点的四个点都位于 Canvas 正中央。在顶部工具栏点击 Rect Tool(矩形工具),可以看到如下图所示的锚点的位置。

锚点的作用是,当游戏窗口分辨率发生变化时。UI 系统会自动根据锚点重新调整位置。

选中 Image 对象,然后在 Inspector 视图中点击 Rect Transform 右侧的设置图标并选择Reset, 然后切换到 Game 视图,可以看到 Image 对象位于视图的正中央。尝试改变 Game视图的大小和分辨率,可以看到 Image 对象一直处于视图的正中心。通过锚点预设将 4 个锚点移动到 Canvas 左下角,此时修改 Garme 视图的大小,可发现 Image 对象会和左下角一直保持固定的距离。此时,Image 对象并没有显示任何图片。因此,我们需要设置 Image 对象的Source Image属性来显示自定义的图片。首先需要将图片在 Unity 中转换为 Sprite 类型。在Unity 编辑器的 Project 视图中选中图片资源,接着在 Inspector 视因中找到 Texture Type,从 Texture Type 的下拉列表框中选中Sprite(2D and UI),然后点击右下角的 Apply 按钮开始转换,如图所示

随后将 Image 组件中的 Source Image 参数设置为该图片。

在 Hierarchy 视图中选中 Image 对象,然后在 Inspector 视图中找到 Image(Script) 组件,然后点击 Source Image 右侧的小圆点并选择刚才的图片,或者直接从 Assets 目录中将图片拖到 Source Image 属性处,如图所示。

Color 参数可设置图片的颜色,白色为透明,也可以尝试设置其他颜色。 lmageType 选项中有多个选项,如图所示。Image Type 属性值默认设置为 Simple,如果将该属性值修改为 Filled,就可以做一些有趣的事情了,比如利用图片显示载人进度。其中,Fill Amount 为填充图片的百分比。也可以理解为进度。在脚本中,我们可通过 Image 组件的 Fill Amount 属性来访问该数值。Fill Method 下有多重效果。

3 TextMeshPro

TextMeshPro 曾经是 Unity Asset Store 中功能强大的第三方文本 UI 对象插件,如今被 Unity 整合进官方版本,并且更推荐使用 TextMeshPro

在 Hierarchy 视图中的空白处右击->UI->Text-TextMeshPro,从而添加一个新的 TextMeshPro 对象。如果场景中已有其他 UI 元素,可以右击 Hierarchy 视图中的 Canvas 对象,选择 UI-Text-TextMeshPro 选项,从而在原有的 Canvas 对象下添加一个新的 TextMeshPro 子对象。

在 Inspector 视图中可以看到,TextMeshPro 对象下除了 Rect Transform 组件,还有一个 TextMeshPro 组件。Text 组件决定了显示的文字内容和排版,如图所示。

TextMeshPro 组件的属性说明如下。

  1. TextMeshPro 属性的文本框中可以输人自己想要在场景中显示的文字,比如这里输人了“欢迎使用 Unity 字样。

  2. Font Asset 属性为字体。如果开发者希望使用自定义字体,只需将字体添加到 Assets 目录中,然后在这里选择自定义的字体就好,十分方便。

  3. Font Style 字体风格。开发者可以将其设置为 Normal (正常)、Bold(加粗)、Italic(斜体)、Bold and Italic(加粗斜体)等风格。

  4. Font size 为字体尺寸。如果开发者设置的字体尺寸超过了 Rect Transform 中的 Width 和 Height 所设置的范围,可能会导致内容无法显示。

  5. Alignment 属性为对齐方式,这里选择的是完全居中。需要注意的是,这里设置的效果仅对当前 Text 对象生效。场景中每个 Text 对象的对齐方式只会作用于自身。

4 Button

首先了解一下 Button 的用法。在 Hierarchy 视图中右击->UI->Button-TextMeshPro,即可添加-个 Button 对象,如图所示可以看到,Button 对象默认有一个 Text子对象

在游戏中默认的显示效果如下图所示

运行场景后,点击 Button,Button 的颜色会发生轻微的变化。在 Inspector 视图中可以看到,Button 由 Rect Transform、Canvas Renderer、Image、Button等组件构成。其中,Rect Transform 组件用于控制 Button 的位置和大小等。Canvas Renderer 组件用于设置渲染风格,Image 组件用于显示按钮的背景图,Button 组件则用于接收鼠标点击事件等

在 Button 组件中,参数 Interactable 用于选择是否可交互,如果取消勾选将变为 Disabled 状态。此时,按钮外形会发生变化,同时不再接收鼠标点击事件。参数 Transition 表示按钮各个状态下的外观变化,默认为 Color Tint, 也就是说拨钮的不同状态完全可通过颜色来表示。Normal Color表示正常情况下按钮的颜色;Highlighted Color表示鼠标移动到按钮上但并没有点击时的颜色,默认和 Normal Color 一致,都为白色:Pressed Color 表示按钮被按下时的颜色,Disabled Color表示按钮不可用时的颜色。

Button 组件属性下 方的 Navigation(按钮导航)包含各个按钮之间的层次关系。当玩家选择按钮时,Navigation 起到在各个按钮之间导航的作用。例如第一个按钮下方存在第二个按钮,当选中第一个按钮并拉下方向键时,第一个按钮的选中状态会被取消,同时第二个按钮进入选中状态,当然前提是这些按钮都开了导航功能。

在 Button 组件属性的最下方是一个 OnClick() 属性,也就是点击右下角的加号还可以添加新的按钮事件。

为了更好地理解按钮的用法,我们将用一个示例来说明。

示例:添加按钮事件

添加一个新的按钮,当点击按钮时,改变第4节中所添加图片的颜色和文字内容。

首先回到 UITest 项目,在 Assets 目录中新建一个子文件夹,将其命名为 Scripts。 在该文件夾下新建一个 C#脚本,将其命名为 MyUIEvents。双击脚本文件在 Visual Studio 中打开,更改该脚本的代码如下所示。

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI; // 1.引入 UI 相关命名空间

public class MyUIEvent : MonoBehaviour
{

    // 2.定义一个 Image 对象
    public Image myImage;

    // 3.定义一个 Text 对象
    public TMP_Text myText;

    // 4.定义更改图片的方法
    public void ChangeImage()
    {
        // 5.更改图片颜色
        myImage.color = Color.red;

        Debug.Log("图片的颜色变了");
    }

    public void ChangText()
    {
        // 5.重新设置文本内内容
        myText.text = "Hello Button";

        Debug.Log("文字内容改变");
    }
}

回到 Unity 编辑器。在 Hierarchy 视图中的空白处右击,选样 Creat Empty。创建一个空白的游戏对象,并将其命名为 UIController。在 Inspector 视图中点击 Add Component 钮,在下拉列表框中依次进择 Scripts--My UI Event,以而将 My UI Event 脚本关联到该对象上。接着将 My UI Events 脚本组件的 My Image 和 My Text 参数分别设置为 Canvas 下的 Image 和 Text,如下图所示

选中 Canvas 下的 Button 对象,在 Inspector 视图的最下方点击右侧的加号添加一个按钮事件,将左侧 Runtime Only 下方的对象设置为刚才创建的 UIController 对象。随后打开 No Function下拉列表框,选择 MyUIEvents - Changelmage() 方法,如下图所示接下来使用相同的方法 为 Button 对象再添加一个按钮事件,然后选择 ChangeText() 方法。这样就成功地为 Button 对象绑定了两个方法,每次点击按钮时,都会执行 Changelmage()和 ChangeText()方法。

运行场景并点击 Apply 按钮后,场景中的图片和文本对象都发生了改变,同时 Console 中输出对应的文字,如图所示。

如果在某些场景中需要使普通 Image 对象变得可交互,只需要为 Image 对象添加 Button 组件。如果无法点击场景中的按钮,请查看 Hierarchy 视图中是否有 Event System 对象,如果没有则需要手动创建,这是因为 Unity 中的所有 UI 交互事件都依赖与 Event System。

5 Toggle

Toggle对象的作用类似于开关。接下来,将使用 Toggle 对象控制 Button 对象。当 Toggle 开启时,即可点击 Button 对象并产生交互,反之则不能。

在 Hierarchy 视窗中的 Canvas 对象下添加一个新的 Toggle 对象,并将其拖放到界面中适合的位置。

默认情况下,Toggle 对象有两个子对象 :Background 和 Label。二者用于控制 Toggle 对象的外形,其中 Background 用于设置 Toggle 对象的背景,而 Lable 用于设置 Toggle 对象的说明文字。Toggle 对象本身有一个 Toggle 组件,如图所示

Toggle 组件中的属性大部分和 Button 组件相同,主要区别在于 Toggle 组件下方的Is On属性和时间的改变。当 Is On 属性为勾选状态是,表明此 Toggle 组件为开启状态,否则为关闭状态。且最下方的默认时间不是 OnClick(),而是 On Value Changed状态,参数为 Boolean。

实例:使用 Toggle 对象

当场景中的 Toggle 被点击时,Is On 属性的状态会发生改变。我们可以通过 Is On 属性来决定场景中某些组件是否可用。

回到 UITest 项目,双击打开之前所创建的 MyUIEvents 脚本,更改其中的代码

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI; // 1.引入 UI 相关命名空间

public class MyUIEvent : MonoBehaviour
{

    // 2.定义一个 Image 对象
    public Image myImage;

    // 3.定义一个 Text 对象
    public TMP_Text myText;

    // 7.定义一个 Toggle 对象
    public Toggle myToggle;

    // 8. 定义一个 Button 对象
    public Button myButton;

    // 4.定义更改图片的方法
    public void ChangeImage()
    {
        // 5.更改图片颜色
        myImage.color = Color.red;

        Debug.Log("图片的颜色变了");
    }

    public void ChangText()
    {
        // 6.重新设置文本内内容
        myText.text = "Hello Button";

        Debug.Log("文字内容改变");
    }

    // 9.定义是否启用按钮对象的方法
    public void OnToggleChanged()
    {
        // 10.如果 toggle 控件开启
        if (myToggle.isOn)
        {
            // 11.启用按钮
            myButton.interactable = true;
        }
        else
        {
            // 12.否则禁用按钮
            myButton.interactable = false;
        }
    }
}

在 OnToggleChanged 方法中,我们通过 Toggle 对象的 is On 属性来修改 Button 的状态。需要注意的是,点击 Toggle 组件时,Toggle 组件的 is On 值会先改变,再执行 OnValueChanged 中绑定的事件。回到 Unity 编辑器,在 Hierarchy 视图中选中示例 4 中所创建的 UIController 对象,然后在 Inspector 视图设置 UIEvents 脚本组件中 Toggle 和 Button 对应的 UI 对象

并在 Toggle 对象的 OnValueChanged 中添加新按钮事件,将其对应的方法设置为 OnToggleChanged。此外,在 Hierarchy 视图中选中 Button 对象,然后在 Inspector 视图中取消勾选 Button 对象下的 Interactable 属性,以而让 Button 对象在默认状态下不可交互。在 Hierarchy 视图中选中 Button 对象,在 Inspector 视图的最下方点击右侧的加号添加一个按钮事件,将左侧 Runtime Only 下方的对象设置为刚才创建的 UIController 对象。随后点击 No Function 打开下拉列表框,此时我们能访问 UIController 对象下各个组件的 Public 方法,然后选择 MyUIEvents - OnToggleChanged 方法。运行场景,点击 Toggle 时,Button 的状态也会随之改变。

6 Slider

Slider 也就是滑动条,可用于控制音量等。

首先在场景中添加一个 Slider 对象。Slider 对象也是由数个 image 对象组成的,可拖动的圆环和背景进度条都是一个 Image 对象。我们可通过更换 Image 中的图片来更改 slider 对象的样式。Slider 组件的參数设置如图所示

Slider 的上半部分参数基本和 Button 一致。下半部分参数中,Fill RectHandle Rect分别指向 Slider 对象的两个子对象,Fill Rect 指向背景进度条,Handle Rect 指向圆环度条。

Direction 为 Slider 移动的方向,默认移动方向为从左至右 (Left To Right)。Min Value表示 Slider 控制范围的最小值,Max Value 表示 Slider 控制范围的最大值,二者都可以通脚本组件进行设置。Whole Numbers 表示数值必领为整数,例如最小值为 0,最大值为 10,如果勾选了 Whole Numbers, 那么 slider 的值只能是 0~10 间的整数,不能是小数。

Value 是 Slider 的具体数值,在脚本中通过 slider.value进行访问。最下方的 On Value Changed 为 Slider 值变化时触发的事件。

实例:添加 Slider 对象

接下来,使用 Slider 改变 Text 对象中文字大小。回到 UITest 项目,在 Hierarchy 视图中选中 Slider 对象,然后在 Inspector 视图中设置 Slider(Script 组件)下的 Min Value 为 1,Max Value 为 50,勾选 Whole Numbers。打开 My UI Events 脚本。更改代码如下:

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI; // 1.引入 UI 相关命名空间

public class MyUIEvent : MonoBehaviour
{

    // 2.定义一个 Image 对象
    public Image myImage;

    // 3.定义一个 Text 对象
    public TMP_Text myText;

    // 7.定义一个 Toggle 对象
    public Toggle myToggle;

    // 8. 定义一个 Button 对象
    public Button myButton;

    // 13.定义一个 Slider 对象
    public Slider mySlider;

    // 4.定义更改图片的方法
    public void ChangeImage()
    {
        // 5.更改图片颜色
        myImage.color = Color.red;

        Debug.Log("图片的颜色变了");
    }

    public void ChangText()
    {
        // 6.重新设置文本内内容
        myText.text = "Hello Button";

        Debug.Log("文字内容改变");
    }

    // 9.定义是否启用按钮对象的方法
    public void OnToggleChanged()
    {
        // 10.如果 toggle 控件开启
        if (myToggle.isOn)
        {
            // 11.启用按钮
            myButton.interactable = true;
        }
        else
        {
            // 12.否则禁用按钮
            myButton.interactable = false;
        }
    }

    // 14.定义一个方法,使滑动条数值更改时更改字体大小
    public void OnSliderValueChanged()
    {
      // 15.将文本对象的字体大小与滑动条的数值相匹配
      myText.fontSize = (int)mySlider.value;
    }
}

接下来回到 Unity 编辑起,在 Hierarchy 视图中选中 UIController 对象,然后在 Inspector 视图中设置 Slider 的值,并在 Slider 对象中用之前学过的方法绑定时间。运行场景,拖动 Slider 时可以看到,Text 对象的文字大小也会随之变化。