UnityUI 系统 - UGUI

任务 28:DrawCall 知识点

知识点一 DrawCall 的概念

字面理解,DrawCall 就是绘制呼叫的意思
表示 CPU(中央处理器)通知 GPU(图形处理器-显卡)

DrawCall 概念,就是 CPU(处理器)准备好渲染数据(顶点,纹理,法线,Shader 等等)后
告知 GPU(图形处理器-显卡),开始渲染(将命令放入命令缓冲区)的命令。
简单来说:一次 DrawCall 就是 CPU 准备好渲染数据通知 GPU 渲染的这个过程。

如果游戏中 DrawCall 数量较高会影响 CPU 的效率,最直接的感受就是游戏会卡顿

举例说明:以拷贝文件来进行类比
假设我们创建 10000 个小文件,每个文件大小为 1kb,然后把这些文件拷贝到另一个文件夹中
你会发现,即使这些文件加起来不超过 10MB,但是拷贝花费的时间是很长的
如果我们单独创建 1 个 10MB 的文件拷贝到另一个文件夹,基本可以瞬间拷贝完毕

为什么会这样呢?
因为每一个文件赋值动作都需要很多额外的操作,比如分配内存,创建数据等等
这些操作就会带来一些额外的性能开销
简单理解:文件越多额外开销就越大

渲染过程和上面的例子很类似,每次 DrawCall,CPU 都需要准备很多数据发送给 GPU
那么如果 DrawCall 越多那么额外开销就越大,其实 GPU 的渲染效率是很强大的,往往影响渲染效率的
都是因为 CPU 提交命令的速度
如果 DrawCall 太多 CPU 就会把大量时间花在提交 DrawCall 上 造成 CPU 过载,游戏卡顿

知识点二 如何降低 DrawCall 数量

在 UI 层面上
小图合大图——>即多个小 DrawCall 变一次大 DrawCall

知识点三 制作 UI 时降低 DrawCall 的技巧

  1. 通过 NGUI Panel 上提供的 DrawCall 查看工具
  2. 注意不同图集之间的层级关系
  3. 注意 Label 的层级关系

任务 1:UGUI 概述

UGUI 是什么

UGUI 是 Unity 引擎内自带的 UI 系统官方称之为: Unity Ul

是目前 Unity 商业游戏开发中使用最广泛的 UI 系统开发解决方案它是基于 Unity 游戏对象的 UI 系统,只能用来做游戏 UI 功能不能用于开发 Unity 编辑器中内置的用户界面。

为什么要学 UGUI

UlElements、Unity UI (UGgI) 、IMGUI (GUI)是 Unity 官方提供的 UI 系统

运行时开发 UI 运行时游戏 UI Unity Editor
UIElements 待定 待定 T
UnityUI T T 不可用
IMGUI 用于调试 不推荐 T

$\textcolor{OrangeRed}{UGUI是目前市面上最流行的Unity游戏UI开发方案,大部分的商业游戏公司都是使用UGUI进行开发的。}$

所以它相对 GUl 和 NGUI 来说,更加的重要,所以我们必须学习掌握他们,它是就业==必备技能==

再次强调 U 系统的重要性

UI 是游戏中最重要的部分。

不管是 2D 游戏还是 3D 游戏几乎所有游戏功能都和 UI 有关系所以它是极其重要的知识点

UI 开发几乎会伴随你的整个游戏开发生涯

主要学习方式

理论+习题+实践

  • 理论:语法知识
  • 习题:基于知识点的针对性习题
  • 实践:基于知识点的小项目实践

主要学习内容

  • 基本控件使用
  • 复合控件使用
  • 一些特殊功能的使用
  • 使用 UGUI 进行功能开发

UGUI 基础

六大基础组件

任务 2:六大基础组件概述

主要学习内容

  1. 如何在 Unity 中使用 UGUI
  2. 六大基础组件在哪里
  3. 六大基础组件概述

如何在 Unity 中使用 UGUI

直接在 Hierarchy 窗口中右键选择 UI,UI 中所有内容都是 UGUI 相关控件

六大基础组件在哪里

Canvas 对象上依附的:

  • Canvas
  • Canvas Scaler
  • Graphic Raycaster
  • RectTransform

EventSystem 对象上依附的:

  • EventSystem
  • Standalone Input Module

六大组件概述

Canvas 对象上依附的:

  • Canvas:画布组件,主要用于渲染 UI 控件
  • Canvas Scaler:画布分辨率自适应组件,主要用于分辨率自适应
  • Graphic Raycaster:射线事件交互组件,主要用于控制射线响应相关
  • RectTransform:对象位置锚点控制组件,主要用于控制位置和对其方式

EventSystem 对象上依附的:

  • EventSystem
  • Standalone Input Module
  • 玩家输入事件响应系统和独立输入模块组件,主要用于监听玩家操作

总结

  1. 如何在 Unity 中使用 UGUI——无需引入内容,直接右键使用
  2. 六大基础组件在哪里——Canvas 对象和 EventSystem 对象上
  3. 六大基础组件概述——六大组件分别有什么作用

任务 3:Canvas——渲染模式的控制

Canvas 组件用来干啥

Canvas 的意思是画布。

它是 UGUI 中所有 UI 元素能够被显示的根本,它主要负责渲染自己的所有 U 子对象。

如果 UI 控件对象不是 Canvas 的子对象,那么控件将不能被渲染。

我们可以通过修改 Canvas 组件上的参数修改渲染方式。

场景中可以有多个 Canvas 对象

可以分别管理不同画布的渲染方式,分辨率适应方式等等参数

如果没有特殊需求,一般情况场景上一个 Canvas 即可

Canvas 的三种渲染方式

  • Screen Space - Overlay:屏幕空间,覆盖模式,UI 始终在前,常用于 2D 游戏
  • Screen Space - Camera:屏幕空间,摄像机模式,3D 物体可以显示在 UI 之前
  • World Space:世界空间,3D 模式

Screen Space - Overlay

image-20230418114540063

覆盖模式,UI 始终显示在场景内容前方

  • Pixel Perfect:是否开启无锯齿精确渲染(性能换效果)
  • SortOrder:排序层编号(用于控制多个 Canvas 时的渲染先后顺序)
  • TargetDisplay:目标设备(在哪个显示设备上显示)
  • Additional Shader Channels:其他着色器通道,决定着色器可以读取哪些数据

Screen Space - Camera

摄像机模式,3D 物体可以显示在 UI 之前(常用)

一般来说,我们不使用主摄像机作为 RenderCamera,而是使用一个单独的 Camera,方便管理 UI 的显示。

并且这个 Camera 需要设置为 DepthOnly

  • RenderCamera:用于渲染 UI 的摄像机(如果不设置将类似于覆盖模式)
  • Plane Distance:UI 平面在摄像机前方的距离,类似整体 Z 轴的感觉
  • Sorting Layer:所在排序层
  • Order in Layer:排序层的序号

World Space

3D 模式,可以把 UI 对象像 3D 物体一样处理,常用于 VR 或者 AR

一般来说,我们需要通过主摄像机与场景当中物体直接交互,所以 Event Camera 一般为主摄像机。

  • Event Camera:用于处理 UI 事件的摄像机(如果不设置,不能正常注册 UI 事件)

总结

  1. Canvas 组件用来干啥 — 画布组件,用于渲染显示 UI 控件,UI 控件必须作为子对象

  2. 场景中可以有多个 Canvas 对象 — 不同的渲染和分辨率适应方式(不常用)

  3. Canvas 组件的 3 种渲染方式

    • 覆盖模式:UI 始终显示在最前面

    • 摄像机模式:3D 物体可以显示在 UI 之前

    • 3D 模式:用于制作 3DUI,在 VR 和 AR 中常用,游戏中的 3D UI 效果才使用

任务 4:CanvasScaler——必备知识

CanvasScaler 是什么

CanvasScaler 意思是画布缩放控制器
它是用于分辨率自适应的组件

它主要负责在不同分辨率下 UI 控件大小自适应
它并==不负责位置==,位置由之后的 RectTransform 组件负责

它主要提供了三种用于分辨率自适应的模式
我们可以选择符合我们项目需求的方式进行分辨率自适应

CanvasScaler 学前准备

屏幕分辨率:

Game 窗口中的 Stats 统计数据窗口,看到的当前“屏幕”(Screen)分辨率,会参与分辨率自适应的计算

画布大小和缩放系数:

选中 Canvas 对象后,在 RectTransform 组件中看到的宽高(Width、Height)和缩放(Scale)。

无论缩放模式是什么,这个公式都是如此。

$\textcolor{OrangeRed}{宽高*缩放系数 = 屏幕分辨率}$

Reference Resolution:

Canvas Scaler 组件下的 Reference Resolution (参考分辨率)
是在缩放模式的宽高模式中出现的参数,参与分辨率自适应的计算。

汇总

  • 屏幕分辨率——当前设备的分辨率,编辑器下 Game 窗口中可以查看到
  • 参考分辨率——在其中一种适配模式中出现的关键参数,参与分辨率自适应的计算
  • 画布宽高和缩放系数——分辨率自适应会改变的参数,通过屏幕分辨率和参考分辨率计算而来

分辨率大小自适应——通过一定的算法以屏幕分辨率和参考分辨率参与计算得出缩放系数,该结果会影响所有 UI 控件的缩放大小。

必须理解

分辨率大小自适应主要就是通过不同的算法计算出一个缩放系数
用该系数去缩放所有 UI 控件
让其在不同分辨率下达到一个较为理想的显示效果

说人话:屏幕大的时候,按钮大,屏幕小的时候,按钮小

CanvasScaler 的三种适配模式

  • Constant Pixel Size(恒定像素模式):无论屏幕大小如何,UI 始终保持相同像素大小
  • Scale With Screen Size(缩放模式,常用):根据屏幕尺寸进行缩放,随着屏幕尺寸放大缩小
  • Constant Physical Size(恒定物理模式):无论屏幕大小和分辨率如何,UI 元素始终保持相同物理大小

总结

  1. CanvasScaler 组件用来干啥

    分辨率大小自适应

  2. CanvasScaler 组件学前准备

    画布尺寸和缩放、屏幕分辨率、参考分辨率

  3. CanvasScaler 的三种适配方式

    • 恒定像素模式
    • 缩放模式
    • 恒定物理模式

任务 5:CanvasScaler——恒定像素模式

知识回顾

  1. CanvasScaler 组件用来干啥

    分辨率大小自适应

  2. CanvasScaler 组件学前准备

    画布尺寸和缩放、屏幕分辨率、参考分辨率

  3. CanvasScaler 的三种适配方式

    • 恒定像素模式
    • 缩放模式
    • 恒定物理模式

CanvasScaler 的三种适配模式

  • Constant Pixel Size(恒定像素模式):无论屏幕大小如何,UI 始终保持相同像素大小
  • Scale With Screen Size(缩放模式):根据屏幕尺寸进行缩放,随着屏幕尺寸放大缩小
  • Constant Physical Size(恒定物理模式):无论屏幕大小和分辨率如何,UI 元素始终保持相同物理大小

模式一:恒定像素模式

Constant Pixel Size

  • Scale Factor:缩放系数,按此系数缩放画布中的所有 UI 元素

  • Reference Pixels Per Unit:

    单位参考像素,多少像素对应 Unity 中的一个单位(默认一个单位(一米)为 100 像素)

    图片设置中的 Pixels Per Unit 设置,会和该参数一起参与计算

恒定像素模式计算公式

$\textcolor{OrangeRed}{UI原始尺寸 }$ = 图片大小(像素)/ (Pixels Per Unit / Reference Pixels Per Unit)

比如一个图片的 Width 是 98,Pixels Per Unit = 100,Reference Pixels Per Unit = 100

那么它的 Native Size Width = 98 / 100 / 100 = 98

Pixels Per Unit:导入 Sprite,选择模式为 Sprite,就会显示。

总结

恒定像素模式
它不会让 UI 控件进行分辨率大小自适应
会让 UI 控件始终保持设置的尺寸大小显示

一般在进行游戏开发极少使用这种模式
除非通过代码计算来设置缩放系数

任务 6:CanvasScaler——缩放模式

知识回顾

恒定像素模式

它不会让 UI 控件进行分辨率大小自适应
会让 UI 控件始终保持设置的尺寸大小显示
可以通过缩放系数参数设置 UI 控件的缩放大小

一般在进行游戏开发极少使用这种模式
除非通过代码计算来设置缩放系数

模式二:缩放模式

Scale With Screen Size(缩放模式):根据屏幕尺寸进行缩放,随着屏幕尺寸变化来放大缩小

Scale With Screen Size

按屏幕大小缩放的模式(简称:缩放模式)

  • Reference Resolution:参考分辨率(美术同学出图的标准分辨率)。

    缩放模式下的所有匹配模式都会基于参考分辨率进行自适应计算

    填写分辨率的标准:根据产品定位,选择市面上最常用的分辨率出图。

  • Screen Match Mode:屏幕匹配模式,当前屏幕分辨率宽高比不适应参考分辨率时,用于分辨率大小自适应的匹配模式

缩放模式中的三种匹配方式

  1. Expand:水平或垂直拓展画布区域,会根据宽高比的变化来放大缩小画布,可能有黑边
  2. Shrink:水平或垂直裁剪画布区域,会根据宽高比的变化来放大缩小画布,可能会裁剪
  3. Match Width Or Height:以宽高或者二者的平均值作为参考来缩放画布区域

Expand

拓展匹配

将 Canvas Size 进行宽或高扩大,让他高于参考分辨率

计算公式:
缩放系数 = Mathf.Min(屏幕宽/参考分辨率宽,屏幕高/参考分辨率高);
画布尺寸 = 屏幕尺寸 / 缩放系数

表现效果:最大程度的缩小 UI 元素,保留 UI 控件所有细节,可能会留黑边

将 Canvas Size 进行宽或高扩大,让他高于参考分辨率。

Expand 拓展模式举例说明

计算公式:

缩放系数 = Mathf.Min(800/1920,600/1080) = Mathf.Min(0.41667,0.5555) ≈ 0.41667

画布尺寸 =(800,600) / 0.41667 ≈(1920,1440)

Shrink

收缩匹配

将 Canvas Size 进行宽或高收缩,让他低于参考分辨率
计算公式:
缩放系数 = Mathf.Max(屏幕宽/参考分辨率宽,屏幕高/参考分辨率高);
画布尺寸 = 屏幕尺寸 / 缩放系数

表现效果:最大程度的放大 UI 元素,让 UI 元素能够填满画面,可能会出现裁剪

Shrink 收缩模式举例说明

将 Canvas Size 进行宽或高收缩,让他低于参考分辨率
计算公式:
缩放系数 = Mathf.Max(800/1920,600/1080) = Mathf.Max(0.41667,0.5555) ≈ 0.5555
画布尺寸 =(800,600) / 0.5555 ≈(1440,1080)

Match Width Or Height

宽高匹配

以宽高或者二者的某种平均值作为参考来缩放画布

Match:确定用于计算的宽高匹配值

计算公式描述

1
2
3
4
5
6
//在取平均值之前,我们先取相对宽度和高度的对数
float logWidth = Mathf.Log(屏幕宽 / 参考分辨率宽, 2);
float logHeight = Mathf.Log(屏幕高 / 参考分辨率高, 2);
//在对数空间中变换是为了获得更好的性能以及更准确的结果
float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight);
scaleFactor = Mathf.Pow(2, logWeightedAverage);

Match Width Or Height 极值举例说明对数运算的好处

假设
Match 值为:0.5
参考分辨率为:800 * 600
屏幕分辨率为:400 * 1200

不用对数计算:
参考分辨率宽 800:屏幕分辨率宽 400 = 2
参考分辨率宽 600:屏幕分辨率高 1200 = 0.5
在正常空间中平均值:(2+0.5)/2=1.25

使用对数计算:

1
2
3
4
float logWidth = Mathf.Log(屏幕宽 / 参考分辨率宽, 2);
LogWidth = Log2(400/800) = -1
float logHeight = Mathf.Log(屏幕高 / 参考分辨率高, 2);
logHeight = Log2(1200/600) = 1

在对数空间中平均值:(-1+1)/2=0

1
2
3
4
float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight);
logWeightedAverage = Mathf.Lerp(-1,1, 0.5);
scaleFactor = Mathf.Pow(2, logWeightedAverage);
scaleFactor = Mathf.Pow(2, 0) = 1

表现效果

主要用于只有横屏模式或者竖屏模式的游戏
竖屏游戏:Match = 0
将画布宽度设置为参考分辨率的宽度
并保持比例不变,屏幕越高可能会有黑边

横屏游戏:Match = 1
将画布高度设置为参考分辨率的高度
并保持比例不变,屏幕越长可能会有黑边

总结

游戏开发一般使用
Scale With Screen Size 缩放模式

使用建议
存在横竖屏切换选择:
Expand(拓展匹配,有黑边)和 Shrink(收缩匹配,有裁剪)

不存在横竖屏切换(定死的横屏或者竖屏游戏):
Match Width or Height(宽高匹配)
横屏游戏 Match = 1
竖屏游戏 Match = 0

任务 7:CanvasScaler——恒定物理模式

知识回顾

  • Constant Pixel Size 恒定像素模式
  • Scale With Screen Size 缩放模式

游戏开发一般使用缩放模式,会帮助我们进行分辨率大小自适应

模式三:恒定物理模式

Constant Physical Size(恒定物理模式):无论屏幕大小和分辨率如何,UI 元素始终保持相
同物理大小

Constant Physical Size

恒定物理尺寸模式(简称:恒定物理模式)

DPI:(Dots Per Inch,每英寸点数)图像每英寸长度内的像素点数

Physical Unit:物理单位,使用的物理单位种类
Fallback Screen DPI:备用 DPI,当找不到设备 DPI 时,使用此值
Default Sprite DPI:默认图片 DPI

物理单位种类

单位种类 与 1 英寸的关系
Centimeters(厘米 cm) 2.54
Millimeters(毫米 mm) 25.4
Inches(英寸) 1
Points(点) 72
Picas(皮卡) 6

计算公式

根据 DPI 算出新的 Reference Pixels Per Unit (单位参考像素)

1
新单位参考像素 = 单位参考像素 * Physical Unit / Default Sprite DPI

再使用模式一:恒定像素模式的公式进行计算

1
原始尺寸 = 图片大小(像素)/ (Pixels Per Unit / 新单位参考像素)

恒定像素模式和恒定物理模式区别

相同点:他们都不会进行缩放,图片有多大显示多大,使用他们不会进行分辨率大小自适应

不同点:相同尺寸不同 DPI 设备像素点区别,像素点越多细节越多

同样为 5 像素,DPI 较低的设备上看起来的尺寸可能会大于 DPI 较高的设备

总结

恒定物理模式

它不会让 UI 控件进行分辨率大小自适应,会让 UI 控件始终保持设置的尺寸大小显示,而且会根据设备 DPI 进行计算,让在不同设备上的显示大小更加准确。

一般在进行游戏开发极少使用这种模式。

任务 8:CanvasScaler——3D 模式

知识回顾

Constant Pixel Size(恒定像素模式):无论屏幕大小如何,UI 始终保持相同像素大小

Scale With Screen Size(缩放模式):根据屏幕尺寸进行缩放,随着屏幕尺寸放大缩小

Constant Physical Size(恒定物理模式):无论屏幕大小和分辨率如何,UI 元素始终保持相
同物理大小

特殊模式:3D 模式

3D 模式何时出现

当 Canvas 的渲染模式设置为
世界空间 3D 渲染模式时

这时 Canvas Scaler 的缩放模式会强制变为
World 3D 世界模式

World 模式 世界模式(简称:3D 模式)

Dynamic Pixels Per Unit:UI 中动态创建的位图(例如文本)中,单位像素数(类似密度)

Reference Pixels Per Unit:单位参考像素,多少像素对应 Unity 中的一个单位(默认一个单
位为 100 像素)

如果想要 UI 清晰,那么改大 Dynamic Pixels Per Unit 参数

总结

3D 模式
只有在 3D 渲染模式下才会启用的模式
主要用于控制该模式下的像素密度

该模式了解即可,一般不修改

任务 9:Graphic Raycater 图形射线投射器组件

Graphic Raycaster 用来干啥

Graphic Raycaster 意思是图形射线投射器
它是用于检测 UI 输入事件的射线发射器

它主要负责通过射线检测玩家和 UI 元素的交互
判断是否点击到了 UI 元素

Graphic Raycaster 参数

  • Ignore Reversed Graphics:是否忽略反转图形
    • 当 UI 控件 X 轴旋转 180 度,或者 Y 轴旋转 180 度时,UI 不能响应。
  • Blocking Objects:射线被哪些类型的碰撞器阻挡(在覆盖渲染模式下无效)
  • Blocking Mask:射线被哪些层级的碰撞器阻挡(在覆盖渲染模式下无效)

总结

  1. Graphic Raycaster 组件用来干啥

    用于 UI 元素的射线检测,用于触发交互

  2. Graphic Raycaster 参数

    • Ignore Reversed Graphics:是否忽略反转图形
    • Blocking Objects:射线被哪些类型的碰撞器阻挡(在覆盖渲染模式下无效)
    • Blocking Mask:射线被哪些层级的碰撞器阻挡(在覆盖渲染模式下无效)

任务 10:EventSystem 和 Standalone Input Module

EventSystem 组件用来干啥

Event System 意思是事件系统

它是用于管理玩家的输入事件并分发给各 UI 控件
它是事件逻辑处理模块
所有的 UI 事件都通过 EventSystem 组件中轮询检测并做相应的执行
它类似一个中转站,和许多模块一起共同协作

如果没有它,所有点击、拖曳等等行为都不会被响应

EventSystem 组件参数

  • First Selected:首先选择的游戏对象,可以设置游戏一开始的默认选择

  • Send Navigation Events:是否允许导航事件(移动/按下/取消)

    就是是否允许使用输入选择 UI

  • Drag Threshold:拖拽操作的阈值(移动多少像素算拖拽)

Standalone Input Module 组件用来干啥的

Standalone Input Module 意思是 独立输入模块
它主要针对处理鼠标/键盘/控制器/触屏(新版 Unity)的输入
输入的事件通过 EventSystem 进行分发
它依赖于 EventSystem 组件,他们两缺一不可

Standalone Input Module 组件参数

  • Horizontal Axis:水平轴按钮对应的热键名(该名字对应 Input 管理器)
  • Vertical Axis:垂直轴按钮对应的热键名(该名字对应 Input 管理器)
  • Submit Button:提交(确定)按钮对应的热建名(该名字对应 Input 管理器)
  • Cancel Button:取消按钮对应的热建名(该名字对应 Input 管理器)
  • Input Actions Per Second:每秒允许键盘/控制器输入的数量
  • Repeat Delay:每秒输入操作重复率生效前的延迟时间
  • ForceModule Active:是否强制模块处于激活状态

总结

EventSystem 和 Standalone Input Module 组件
他们主要是用于输入事件监听管理和分发的
如果没有了他们两
那么 UI 中的所有输入事件将不会被响应

他们的参数相关了解即可,一般情况下不会去修改

任务 11:RectTransform 矩形变化

RectTransform 组件用来干啥

RectTransform 意思是矩形变换
它继承于 Transform
是专门用于处理 UI 元素位置大小相关的组件

Transform 组件只处理位置、角度、缩放
RectTransform 在此基础上加入了矩形相关,将 UI 元素当做一个矩形来处理
加入了中心点、锚点、长宽等属性
其目的是更加方便的控制其大小以及分辨率自适应中的位置适应

RectTransform 组件参数

  • Pivot:轴心(中心)点,取值范围 0~1

  • Anchors(相对父矩形锚点):

    • Min 是矩形锚点范围 X 和 Y 的最小值

    • Max 是矩形锚点范围 X 和 Y 的最大值

    • 取值范围都是 0~1

  • Pos(X,Y,Z):轴心点(中心点)相对锚点的位置

  • Width/Height:矩形的宽高

  • Left/Top/Right/Bottom:矩形边缘相对于锚点的位置;当锚点分离时会出现这些内容

  • Rotation:围绕轴心点旋转的角度

  • Scale:缩放大小

  • 虚线正方形图标:Blueprint Mode(蓝图模式),启用后,编辑旋转和缩放不会影响矩形,只会影响显示内容

  • R 图标:Raw Edit Mode(原始编辑模式),启用后,改变轴心和锚点值不会改变矩形位置

快捷设置面板

点击左上角方框,将出现锚点中心点快捷设置面板

鼠标左键点击其中的选项,
可以快捷设置锚点(9 宫格布局

按住 Shift 点击鼠标左键可以同时设置轴心点(相对自身矩形)
按住 Alt 点击鼠标左键可以同时设置位置

总结

RectTransform 是专门用于控制 UI 尺寸大小对齐方式的组件
我们要搞清锚点、轴心点的作用
它主要可以帮助我们进行分辨率变化时
位置的自适应(9 宫格布局)

我们需要熟练使用该组件

三大基础控件

任务 12:Image 图像控件 知识点

可视组件 - Unity 手册

知识点一 Image 是什么?

Image 是图像组件
是 UGUI 中用于显示精灵图片的关键组件
除了背景图等大图,一般都使用 Image 来显示 UI 中的图片元素

知识点二 Image 参数

图像 (Image) - Unity 手册

image-20230419202315660

  • Source Image:图片来源(图片类型必须是”精灵“类型)
  • Color:图像的颜色
  • Material:图像的材质(一般不修改,会使用 UI 的默认材质)
  • Raycast Target:是否作为射线检测的目标(如果不勾选将不会响应射线检测)
  • Maskable:是否能被遮罩(之后结合遮罩相关知识点进行讲解)
  • Image Type:图片类型
    • Simple-普通模式,均匀缩放整个图片
    • Sliced-切片模式,9 宫格拉伸,只拉伸中央十字区域(可以用来做纯色背景)
      • Pixels Per UnitMultiplier:每单位像素乘数
      • Fill Center:中心填充
    • Tiled-平铺模式,重复平铺中央部分(可以用来做纹理的重复)
    • Filled-填充模式(可以用做技能 CD)
      • Fill Method:填充方式
        • Fill Origin:填充原点
        • Fill Amount:填充量
        • Clockwise:顺时针方向
  • Use Sprite Mesh:使用精灵网格,勾选的话 Unity 会帮我们生成图片网格
  • Preserve Aspect:确保图像保持其现有尺寸
  • Set Native Size:设置为图片资源的原始大小

如何控制 UI 渲染循序?

调整控件对象的顺序即可

知识点三 代码控制

大部分变量都和组件参数一致。

1
2
3
4
5
6
7
Image img = this.GetComponent<Image>();
img.sprite = Resources.Load<Sprite>("ui_TY_fanhui_01");

(transform as RectTransform).sizeDelta = new Vector2(200, 200);
img.raycastTarget = false;

img.color = Color.red;

任务 13:Text 文本控件 知识点

知识点一 Text 是什么

Text 是文本组件
是 UGUI 中用于显示文本的关键组件

知识点二 Text 参数相关

文本 (Text) - Unity 手册

知识点三 富文本

一个 Text 内,通过标签控制文字显示不同风格。

使用富文本,必须勾选 Text 组件当中的 Rich Text

富文本 - Unity 手册

知识点四 边缘线和阴影

为 Text 添加 Outline组件可以为文字添加边缘线

为 Text 添加 Shadow组件可以为文字添加阴影

知识点五 代码控制

1
2
Text txt = this.GetComponent<Text>();
txt.text = "你好,我是盘子";

任务 14:RawImage 原始图像控件 知识点

知识点一 RawImage 是什么

RawImage 是原始图像组件
是 UGUI 中用于显示任何纹理图片的关键组件

它和 Image 的区别是 一般 RawImage 用于显示大图(背景图,不需要打入图集的图片,网络下载的图等等)

RawImage 和 Image 的区别

在 Unity 中,Image 组件用于显示 2D 图像,而 RawImage 组件用于显示未经处理的图像数据。Image 组件可以显示 PNG、JPG 等格式的图像,而 RawImage 组件可以显示任何类型的图像数据,包括从网络下载的图像、视频帧等。

Image 组件可以设置 Sprite 属性来显示图像,而 RawImage 组件可以设置 Texture 属性来显示图像。Sprite 是一个 2D 图像,可以在 Unity 编辑器中创建和编辑。Texture 是一个原始的图像数据,可以通过代码或从文件中加载。

知识点二 RawIamge 参数

原始图像 (Raw Image) - Unity 手册

知识点三 代码控制

1
2
3
RawImage raw = this.GetComponent<RawImage>();
raw.texture = Resources.Load<Texture>("ui_TY_lvseshuzi_08");
raw.uvRect = new Rect(0, 0, 1, 1);

组合控件

任务 15:Button 按钮控件 知识点

知识点一 Button 是什么

Button 是按钮组件
是 UGUI 中用于处理玩家按钮相关交互的关键组件

默认创建的 Button 由 2 个对象组成
父对象——Button 组件依附对象 同时挂载了一个 Image 组件 作为按钮背景图
子对象——按钮文本(可选)

知识点二 Button 参数

按钮 (Button) - Unity 手册

知识点三 代码控制

获取 Button 组件,然后修改组件属性

1
2
3
4
5
Button btn = this.GetComponent<Button>();
btn.interactable = true;
btn.transition = Selectable.Transition.None;

Image img = this.GetComponent<Image>();

知识点四 监听点击事件的两种方式

点击事件:在按钮区域抬起按下一次,就算一次点击。

拖脚本

点击 OnClick 列表下的“+”号,选择 GameObject,就能选择上面的类的函数。

代码添加

1
2
3
4
5
6
7
8
9
10
btn.onClick.AddListener(ClickBtn2);	//ClickBtn2是一个函数
//使用lambda表达式添加
btn.onClick.AddListener(() => {
print("123123123");
});

//移除监听
btn.onClick.RemoveListener(ClickBtn2);
//移除所有监听
btn.onClick.RemoveAllListeners();

练习题

请用现在所学知识,制作一个这样的功能
场景上有一个对象,点击 UGUI 的发射按钮,可以让对象发射一颗子弹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class P10 : MonoBehaviour
{
private GameObject bulletPrefab;
// Start is called before the first frame update
void Start()
{
bulletPrefab = Resources.Load<GameObject>("Prefab/Bullet");
}

public void Fire()
{
GameObject obj = Instantiate(bulletPrefab, transform.position, transform.rotation);
}
}

任务 17:Toggle 开关控件 知识点

知识点一 Toggle 是什么

Toggle 是开关组件
是 UGUI 中用于处理玩家单选框多选框相关交互的关键组件

开关组件 默认是多选框
可以通过配合 ToggleGroup 组件制作为单选框

默认创建的 Toggle 由 4 个对象组成
父对象——Toggle 组件依附
子对象——背景图(必备)、选中图(必备)、说明文字(可选)。

知识点二 Toggle 参数

开关 (Toggle) - Unity 手册

如何制作单选框?

将多个 Toggle 的 Group 选择为同一个拥有 ToggleGroup 组件的 GameObject。

这样,这些 Toggle 就是一组了,并且是互斥的。

知识点三 代码控制

1
2
3
4
5
6
7
8
9
10
11
12
Toggle tog = this.GetComponent<Toggle>();
tog.isOn = true;
print(tog.isOn);

ToggleGroup togGroup = this.GetComponent<ToggleGroup>();
togGroup.allowSwitchOff = false;

//可以遍历提供的迭代器 得到当前处于选中状态的 Toggle
foreach (Toggle item in togGroup.ActiveToggles())
{
print(item.name + " " + item.isOn);
}

知识点四 监听事件的两种方式

和 Button 几乎一样,都可以直接在 Inspector 面板当中选择函数,或者使用 AddListener添加函数。但是事件监听的函数,必须是拥有一个 bool 参数的函数。

注意:关联函数时,应该关联 Dynamic 函数,因为开关是动态变化的。

1
2
3
4
5
6
7
//1.拖脚本
//2.代码添加
tog.onValueChanged.AddListener(ChangeValue2);
tog.onValueChanged.AddListener((b) =>
{
print("代码监听 状态改变" + b);
});

练习题

在上节课的练习题基础上
请用现在所学知识,制作一个这样的功能
场景上对象发射子弹有音效,通过用 UGUI 的 Toggle 开关 控制音效开关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class P10 : MonoBehaviour
{
private GameObject bulletPrefab;
private AudioClip fireClip;
private AudioSource audioSource;
// Start is called before the first frame update
void Start()
{
bulletPrefab = Resources.Load<GameObject>("Prefab/Bullet");
fireClip = Resources.Load<AudioClip>("Sound/Spineboygun");
audioSource = GetComponent<AudioSource>();
audioSource.clip = fireClip;
}

public void Fire()
{
GameObject obj = Instantiate(bulletPrefab, transform.position, transform.rotation);
audioSource.Play();
}

public void CloseOrOpenSound(bool isOn)
{
audioSource.mute = !isOn;
}
}

任务 19:InputField 文本输入控件 知识点

知识点一 InputField 是什么

InputField 是输入字段组件
是 UGUI 中用于处理玩家文本输入相关交互的关键组件

默认创建的 InputField 由 3 个对象组成
父对象——InputField 组件依附对象 以及 同时在其上挂载了一个 Image 作为背景图
子对象——文本显示组件(必备)、默认显示文本组件(必备)

知识点二 InputField 参数

输入字段 (Input Field) - Unity 手册

知识点三 代码控制

1
2
3
InputField input = this.GetComponent<InputField>();
print(input.text);
input.text = "123123123123";

知识点四 监听事件的两种方式

注意:关联函数时,应该关联 Dynamic 函数,因为开关是动态变化的。

1
2
3
4
5
6
7
8
9
10
11
//1.拖脚本
//2.代码添加
input.onValueChanged.AddListener((str) =>
{
print("代码监听 改变" + str);
});

input.onEndEdit.AddListener((str) =>
{
print("代码监听 结束输入" + str);
});

练习题

在上节课的练习题基础上
请用现在所学知识,制作一个这样的功能
场景左上角会显示玩家的姓名,右侧有一个改名按钮
点击改名按钮后会弹出一个改名窗口
在窗口上可以输入你的新名字,点击确定键后,原左上角的名字将改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class InputNamePanel : MonoBehaviour
{
public Text playerName;
public InputField inputField;
public Button confirmBtn;
public Button cancelBtn;
private string backUpName;

private static InputNamePanel _instance;
public static InputNamePanel Instance { get => _instance; }

private void Awake()
{
_instance = this;
}

// Start is called before the first frame update
void Start()
{
inputField.onEndEdit.AddListener(OnEndEdit);
confirmBtn.onClick.AddListener(OnClickCloseBtn);
cancelBtn.onClick.AddListener(OnClickCloseBtn);
gameObject.SetActive(false);
}

private void OnEnable()
{
backUpName = playerName.text;
}

public void OnEndEdit(string str)
{
if (!string.IsNullOrEmpty(str))
{
playerName.text = str;
}
}

public void OnClickCloseBtn()
{
gameObject.SetActive(false);
GamePanel.Instance.gameObject.SetActive(true);
}
}

任务 21:Slider 滑动条控件 知识点

知识点一 Slider 是什么

Slider 是滑动条组件
是 UGUI 中用于处理滑动条相关交互的关键组件

默认创建的 Slider 由 4 组对象组成
父对象——Slider 组件依附的对象
子对象——背景图、进度图、滑动块三组对象

知识点二 Slider 参数

滑动条 (Slider) - Unity 手册

知识点三 代码控制

1
2
Slider s = this.GetComponent<Slider>();
print(s.value);

知识点四 监听事件的两种方式

注意:选择关联函数时,需要选择 Dynamic(动态的)函数。

监听函数需要一个 float 类型参数。

1
2
3
4
5
6
//1.拖脚本
//2.代码添加
s.onValueChanged.AddListener((v) =>
{
print("代码添加的监听" + v);
});

练习题

在上节课的练习题基础上
请用现在所学知识,制作一个这样的功能
场景上有一个滑动条,可以通过它控制音效的音量大小

任务 23:ScrollBar 滚动条 知识点

知识点一 Scrollbar 是什么

Scrollbar 是滚动条组件
是 UGUI 中用于处理滚动条相关交互的关键组件

默认创建的 Scrollbar 由 2 组对象组成
父对象——Scrollbar 组件依附的对象
子对象——滚动块对象

一般情况下我们不会单独使用滚动条
都是配合 ScrollView 滚动视图来使用

知识点二 Scrollbar 参数

滚动条 (Scrollbar) - Unity 手册

知识点三 代码控制

1
2
3
Scrollbar sb = this.GetComponent<Scrollbar>();
print(sb.value);
print(sb.size);

知识点四 监听事件的两种方式

注意:需要选择 Dynamic(动态的)函数。

1
2
3
4
5
//1.拖脚本
//2.代码添加
sb.onValueChanged.AddListener((v) => {
print("代码监听的函数" + v);
});

任务 24:ScrollView 滚动视图 知识点

知识点一 ScrollRect 是什么

ScrollRect 是滚动视图组件
是 UGUI 中用于处理滚动视图相关交互的关键组件

默认创建的 ScrollRect 由 4 组对象组成
父对象——ScrollRect 组件依附的对象 还有一个 Image 组件 最为背景图
子对象
Viewport 控制滚动视图可视范围和内容显示
Scrollbar Horizontal 水平滚动条
Scrollbar Vertical 垂直滚动条

知识点二 ScrollRect 参数

滚动矩形 (Scroll Rect) - Unity 手册

知识点三 代码控制

注意:动态创建的内容,一般都放在 Content 下。

1
2
3
4
5
ScrollRect sr = this.GetComponent<ScrollRect>();
//改变内容的大小 具体可以拖动多少 都是根据它的尺寸来的
//sr.content.sizeDelta = new Vector2(200, 200);

sr.normalizedPosition = new Vector2(0, 0.5f);

知识点四 监听事件的两种方式

1
2
3
4
5
6
7
//1.拖脚本
//2.代码添加

sr.onValueChanged.AddListener((vec) =>
{
print(vec);
});

练习题

在上节课的练习题基础上
请用现在所学知识,制作一个这样的功能
有一个背包按钮,点击后可以打开一个背包面板
面板中有一个滚动视图,滚动视图中动态创建 10 个道具图标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class BackpackPanel : MonoBehaviour
{
private static BackpackPanel _instance;
public static BackpackPanel Instance { get => _instance; }


public GameObject itemPrefab;
public ScrollRect scrollRect;



private void Awake()
{
_instance = this;
}

// Start is called before the first frame update
void Start()
{
for (int i = 0; i < 10; ++i)
{
GameObject obj = Instantiate(itemPrefab);
obj.transform.SetParent(scrollRect.content);
RectTransform rectTrans = obj.GetComponent<RectTransform>();
rectTrans.localPosition = new Vector3(0, -rectTrans.rect.width / 2 + -i * rectTrans.rect.width);
}
}
}

任务 26:Dropdown 下拉列表 知识点

知识点一 DropDown 是什么

DropDown 是下拉列表(下拉选单)组件
是 UGUI 中用于处理下拉列表相关交互的关键组件

默认创建的 DropDown 由 4 组对象组成
父对象
DropDown 组件依附的对象 还有一个 Image 组件 作为背景图

子对象
Label 是当前选项描述
Arrow 右侧小箭头
Template 下拉列表选单

知识点二 DropDown 参数

下拉选单 (Dropdown) - Unity 手册

知识点三 代码控制

1
2
3
4
5
6
7
Dropdown dd = this.GetComponent<Dropdown>();

print(dd.value);

print(dd.options[dd.value].text);

dd.options.Add(new Dropdown.OptionData("123123123"));

知识点四 监听事件的两种方式

注意:需要一个 int 参数,表示选中的是哪个选项。

1
2
3
4
5
//1.拖脚本
//2.代码添加
dd.onValueChanged.AddListener((index) => {
print(index);
});

任务 28:图集制作 知识点

知识点一 为什么要打图集

UGUI 和 NGUI 使用上最大的不同是 NGUI 使用前就要打图集
UGUI 可以再之后再打图集

打图集的目的就是减少 DrawCall 提高性能
具体 DrawCall 是什么在 NGUI 课程中已经详细讲解
该节课是免费课 即使没有购买 也可以前往观看

简单回顾 DrawCall
DC 就是 CPU 通知 GPU 进行一次渲染的命令
如果 DC 次数较多会导致游戏卡顿
我们可以通过打图集,将小图合并成大图,将本应 n 次的 DC 变成 1 次 DC 来提高性能

知识点二 在 Unity 中打开自带的打图集功能

在工程设置面板中打开功能
Edit——>Project Setting——>Editor
Sprite Packer(精灵包装器,可以通过 Unity 自带图集工具生成图集)
Disabled:默认设置,不会打包图集

Enabled For Builds(Legacy Sprite Packer):Unity 仅在构建时打包图集,在编辑模式下不会打包图集
Always Enabled(Legacy Sprite Packer):Unity 在构建时打包图集,在编辑模式下运行前会打包图集

Legacy Sprite Packer 传统打包模式 相对下面两种模式来说 多了一个设置图片之间的间隔距离
Padding Power:选择打包算法在计算打包的精灵之间以及精灵与生成的图集边缘之间的间隔距离
这里的数字 代表 2 的 n 次方

Enabled For Build:Unity 进在构建时打包图集,在编辑器模式下不会打包
Always Enabled:Unity 在构建时打包图集,在编辑模式下运行前会打包图集

知识点三 打图集参数注意

精灵图集 - Unity 手册

一般来说,不勾选 AllowRotation(允许旋转图片,以获取更多的图片空间)。

知识点四 代码加载

U2D.SpriteAtlas - Unity 脚本 API

需要需要引用命名空间 using UnityEngine.U2D;

1
2
3
4
//加载图集 注意:需要引用命名空间
SpriteAtlas sa = Resources.Load<SpriteAtlas>("MyAlas");
//从图集中加载指定名字的小图
sa.GetSprite("bk");

练习题

找一些通用资源,做成图集,为之后的课程做准备。

UGUI 进阶

任务 29:UI 事件监听接口 知识点

知识点一 事件接口是用来解决什么问题的

目前所有的控件都只提供了常用的事件监听列表
如果想做一些类似长按,双击,拖拽等功能是无法制作的
或者想让 Image 和 Text,RawImage 三大基础控件能够响应玩家输入也是无法制作的

而事件接口就是用来处理类似问题
让所有控件都能够添加更多的事件监听来处理对应的逻辑。

知识点二 有哪些事件接口

EventSystems.IPointerClickHandler - Unity 脚本 API

支持的事件 - Unity 手册

常用事件接口

  • IPointerEnterHandler - OnPointerEnter - 当指针进入对象时调用 (鼠标进入)

  • IPointerExitHandler - OnPointerExit - 当指针退出对象时调用 (鼠标离开)

  • IPointerDownHandler - OnPointerDown - 在对象上按下指针时调用 (按下)

  • IPointerUpHandler - OnPointerUp - 松开指针时调用(在指针正在点击的游戏对象上调用)(抬起)

  • IPointerClickHandler - OnPointerClick - 在同一对象上按下再松开指针时调用 (点击)

  • IBeginDragHandler - OnBeginDrag - 即将开始拖动时在拖动对象上调用 (开始拖拽)

  • IDragHandler - OnDrag - 发生拖动时在拖动对象上调用 (拖拽中)

  • IEndDragHandler - OnEndDrag - 拖动完成时在拖动对象上调用 (结束拖拽)

不常用事件接口 了解即可

  • IInitializePotentialDragHandler - OnInitializePotentialDrag - 在找到拖动目标时调用,可用于初始化值

  • IDropHandler - OnDrop - 在拖动目标对象上调用

  • IScrollHandler - OnScroll - 当鼠标滚轮滚动时调用

  • IUpdateSelectedHandler - OnUpdateSelected - 每次勾选时在选定对象上调用

  • ISelectHandler - OnSelect - 当对象成为选定对象时调用

  • IDeselectHandler - OnDeselect - 取消选择选定对象时调用

导航相关

  • IMoveHandler - OnMove - 发生移动事件(上、下、左、右等)时调用
  • ISubmitHandler - OnSubmit - 按下 Submit 按钮时调用
  • ICancelHandler - OnCancel - 按下 Cancel 按钮时调用

知识点三 使用事件接口

  1. 继承 MonoBehavior 的脚本继承对应的事件接口,引用命名空间
  2. 实现接口中的内容
  3. 将该脚本挂载到想要监听自定义事件的 UI 控件上

注意:这个事件接口的脚本,必须挂载到 UI 空间上。

知识点四 PointerEventData 参数的关键内容

EventSystems.PointerEventData - Unity 脚本 API

父类:BaseEventData

  • pointerId: 鼠标左右中键点击鼠标的 ID 通过它可以判断右键点击

  • position:当前指针位置(屏幕坐标系)

  • pressPosition:按下的时候指针的位置

  • delta:指针移动增量

  • clickCount:连击次数

  • clickTime:点击时间

  • pressEventCamera:最后一个 OnPointerPress 按下事件关联的摄像机

  • enterEvetnCamera:最后一个 OnPointerEnter 进入事件关联的摄像机

我们可以直接 print 这个 PointerEventData,它重写了 ToString,可以打印很多信息。

总结

好处:
需要监听自定义事件的控件挂载继承实现了接口的脚本就可以监听到一些特殊事件
可以通过它实现一些长按,双击拖拽等功能

坏处:
不方便管理,需要自己写脚本继承接口挂载到对应控件上,比较麻烦

练习题

在上节课的练习题基础上
请用现在所学知识,制作一个这样的功能
长按一个 UI 按钮 0.2s 后开始蓄能,松开按钮后结束蓄能
蓄能满后 HP+10,如果没有松开按钮 继续蓄能

解题思路

将 UI 事件,交给外部来处理。

具体来说,在实现接口的脚本当中声明两个公共事件,在外部获取这个组件,然后添加事件函数,在函数当中处理逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// <summary>
/// 长按按钮脚本 提供两个事件给外部 让外部去处理对应的逻辑
/// </summary>
public class LongPress : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
public event UnityAction upEvent;
public event UnityAction downEvent;

public void OnPointerDown(PointerEventData eventData)
{
downEvent?.Invoke();
}

public void OnPointerUp(PointerEventData eventData)
{
upEvent?.Invoke();
}
}

任务 31:EventTrigger 事件触发器 知识点

知识点一 事件触发器是什么

事件触发器 - Unity 手册

事件触发器是 EventTrigger 组件
它是一个集成了上节课中学习的所有事件接口的脚本
它可以让我们更方便的为控件添加事件监听

知识点二 如何使用事件触发器

EventSystems.EventTrigger - Unity 脚本 API

1.拖曳脚本进行关联

一般来说,是在需要执行事件的 UI 控件上挂载 EventTrigger 组件,然后关联外部的对象,选择函数。

注意:关联的函数必须带有一个参数,参数类型为 BaseEventData。

1
2
3
4
5
public void PointerEnter(BaseEventData eventData)
{
PointerEventData data = eventData as PointerEventData;
print(data.ToString());
}

2.代码添加

EventTrigger 当中拥有一个 public List<Entry> triggers ;,是一个事件的列表。

我们可以直接往这个列表当中添加 Entry(EventTrigger 的内部类),一个 Entry 可以代表一个事件,包含事件类型和一个回调函数。

其中 EventTriggerType代表事件的类型

callBack(TriggerEvent->UnityAction<BaseEventData>)代表回调函数。

EventSystems.Entry - Unity 脚本 API

EventSystems.EventTrigger-triggers - Unity 脚本 API

1
2
3
4
5
6
7
8
9
10
11
12
public EventTrigger et;
// Start is called before the first frame update
void Start()
{
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerClick;
entry.callback.AddListener((data) =>
{
print(data);
});
et.triggers.Add(entry);
}

总结

EventTrigger 可以让我们写更少的代码
可以在面板类中处理面板控件的事件逻辑,更加的面向对象,便于管理

练习题

在上节课的练习题基础上
请用现在所学知识,制作一个这样的功能
制作一个 UGUI 摇杆可以控制场景上的对象移动

麻了,写了垃圾代码,删了

仿照老师的代码写的

Player.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class Player : MonoBehaviour
{
public float moveVelocity = 10.0f; //移动速度
public float roundVelocity = 10.0f; //旋转速度

private Vector3 moveDirection; //移动方向


public void SetDirection(Vector2 dir)
{
//由于传递过来的是Vector2,所以x对应x,z对应y
moveDirection.x = dir.x;
moveDirection.z = dir.y;
}

private void Round()
{
//如果移动方向不为0就旋转
if (moveDirection != Vector3.zero)
{
//让自身旋转至移动方向
//Slerp:将自身旋转值逐渐趋近于目标值
//LookRotation:获取该方向需要旋转的四元数值
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(moveDirection), roundVelocity * Time.deltaTime);
}
}

public void Move()
{
//如果移动方向不为0就移动
if (moveDirection != Vector3.zero)
{
//始终只向前移动
transform.Translate(Vector3.forward * moveVelocity * Time.deltaTime);
}
}

private void Update()
{
Round();
Move();
}
}

VirtualJoystickPanel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class VirtualJoystickPanel : MonoBehaviour
{
public Player player;

public Image imgJoy;
private EventTrigger et;

private void Start()
{
et = GetComponent<EventTrigger>();

//添加事件触发器事件
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.Drag;
entry.callback.AddListener(OnJoyDrag);
et.triggers.Add(entry);

entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.EndDrag;
entry.callback.AddListener(OnEndJoyDrag);
et.triggers.Add(entry);
}

//拖拽事件
private void OnJoyDrag(BaseEventData data)
{
PointerEventData eventData = data as PointerEventData;
//让中心图片的位置,每次加上与上一次位置的偏移
imgJoy.rectTransform.position += new Vector3(eventData.delta.x, eventData.delta.y);
//如果相对于锚点的位置(向量)的模长大于背景图片的半径
if (imgJoy.rectTransform.anchoredPosition.magnitude > 100)
{
//就让它固定在边界
imgJoy.rectTransform.anchoredPosition = imgJoy.rectTransform.anchoredPosition.normalized * 100;
}

//设置玩家方向,让玩家移动
//相对于父节点锚点的位置就是方向
player.SetDirection(imgJoy.rectTransform.anchoredPosition);
}

//拖拽结束事件
private void OnEndJoyDrag(BaseEventData data)
{
//直接将位置设置为zero
imgJoy.rectTransform.anchoredPosition = Vector2.zero;
player.SetDirection(Vector2.zero);
}
}

任务 33:屏幕坐标转 UI 相对坐标 知识点

知识点一 RectTransformUtility 类

RectTransformUtility 公共类是一个 RectTransform 的辅助类
主要用于进行一些 坐标的转换等等操作
其中对于我们目前来说 最重要的函数是 将屏幕空间上的点,转换成 UI 本地坐标下的点

知识点二 将屏幕坐标转换为 UI 本地坐标系下的点

UnityEngine.RectTransformUtility - Unity 脚本 API

1
2
3
4
5
6
7
8
9
10
11
//方法:
//RectTransformUtility.ScreenPointToLocalPointInRectangle
////参数一:相对父对象
////参数二:屏幕点
////参数三:摄像机
////参数四:最终得到的点
////一般配合拖拽事件使用
Vector2 newPos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(imgJoyBg.rectTransform, eventData.position, eventData.enterEventCamera, out newPos);

imgJoy.rectTransform.localPosition = newPos;

注意:最后这里的坐标赋值,应该赋值给 localPosition,因为转换后的坐标是相对于传递的参数相对父对象的坐标。

练习题

在上节课的练习题基础上
修改其中的代码,用新学的屏幕 坐标转 UI 坐标的知识
制作一个 UGUI 摇杆可以控制场景上的对象移动

1
2
3
4
5
//使用RectTransformUtility.ScreenPointToLocalPointInRectangle转换坐标
Vector2 newPos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(imgJoyBg.rectTransform, eventData.position, eventData.enterEventCamera, out newPos);

imgJoy.rectTransform.localPosition = newPos;

任务 35:Mask 遮罩 知识点

遮罩 (Mask) - Unity 手册

知识点一 遮罩是什么

在不改变图片的情况下
让图片在游戏中只显示其中的一部分

知识点二 遮罩如何使用

实现遮罩效果的关键组件时 Mask 组件
通过在父对象上添加 Mask 组件即可遮罩其子对象

注意:

  1. 想要被遮罩的 Image 需要勾选 Maskable
  2. 只要父对象添加了 Mask 组件,那么所有的 UI 子对象都会被遮罩
  3. 遮罩父对象图片的制作,不透明的地方显示,透明的地方被遮罩

练习题

在上节课的练习题基础上
请用现在所学知识,制作一个这样的功能
一个方形的头像在界面上显示成一个圆形

任务 37:模型和粒子显示在 UI 之前 知识点

知识点一 模型显示在 UI 之前

方法一:直接用摄像机渲染 3D 物体

Canvas 的渲染模式要不是覆盖模式
摄像机模式 和 世界(3D)模式都可以让模型显示在 UI 之前(Z 轴在 UI 元素之前即可)

注意:

  1. 摄像机模式时建议用专门的摄像机渲染 UI 相关
  2. 面板上的 3D 物体建议也用 UI 摄像机进行渲染

具体操作步骤

  • 使用一个新的摄像机,设置 Clear Flags 为 Depth Only,Culling Mask 为 UI。
  • 设置 Canvas 组件 Render Mode 为 Screen Space - Camera,选择渲染摄像机为新的摄像机。
  • 将一个物体直接创建在 Canvas 下,设置缩放,调整位置即可在 UI 之前显示。

方法二:将 3D 物体渲染在图片上,通过图片显示

专门使用一个摄像机渲染 3D 模型,将其渲染内容输出到 Render Texture 上
类似小地图的制作方式
再将渲染的图显示在 UI 上

该方式 不管 Canvas 的渲染模式是哪种都可以使用。

具体操作步骤

  • 使用一个新的摄像机,只渲染某一个 Layer,然后某个 Model 创建在这个 Layer 下。
  • 创建 RenderTexture,将新摄像机的 TargetTexture 设置为这个 RenderTexture。
  • 在 Canvas 下创建 RawImage,选择 Texture 为上面创建的 RenderTexture。

知识点二 粒子特效显示在 UI 之前

粒子特效的显示和 3D 物体类似

注意点:
在摄像机模式下时
可以在粒子组件的 Renderer 相关参数中改变排序层 让粒子特效始终显示在其之前不受 Z 轴影响。

任务 38:异形按钮 知识点

知识点一 什么是异形按钮

图片形状不是传统矩形的按钮。

知识点二 如何让异形按钮能够准确点击

方法一 通过添加子对象的形式

按钮之所以能够响应点击,主要是根据图片矩形范围进行判断的
它的范围判断是自下而上的,意思是如果有子对象图片,子对象图片的范围也会算为可点击范围
那么我们就可以用多个透明图拼凑不规则图形作为按钮子对象用于进行射线检测

具体操作步骤

  • 在一个 Image 下创建 Button,把这个 Button 的透明度改成 0
  • 将 Button 组件的 TargetGraphic 设置为异形按钮图片。
  • 在这个 Button 下创建空的 Image,透明度为 0。
  • 组合这些 Image 为异形按钮的形状

方法二 通过代码改变图片的透明度响应阈值

  1. 第一步:修改图片导入参数,开启 Read/Write Enabled 开关

  2. 第二步:通过代码修改图片的响应阈值

该参数含义:指定一个像素必须具有的最小 alpha 值,以变能够认为射线命中了图片
说人话:当像素点 alpha 值小于了 该值 就不会被射线检测了。

1
2
3
4
5
6
public Image btnImage;

void Start()
{
btnImage.alphaHitTestMinimumThreshold = 0.1f;
}

任务 39:自动布局组件 知识点

知识点一 自动布局是什么

虽然 UGUI 的 RectTransform 已经非常方便的可以帮助我们快速布局
但 UGUI 中还提供了很多可以帮助我们对 UI 控件进行自动布局的组件
他们可以帮助我们自动的设置 UI 控件的位置和大小等

自动布局的工作方式一般是
自动布局控制组件 + 布局元素 = 自动布局

自动布局控制组件:Unity 提供了很多用于自动布局的管理性质的组件用于布局
布局元素:具备布局属性的对象们,这里主要是指具备 RectTransform 的 UI 组件

知识点二 布局元素的布局属性

布局元素 (Layout Element) - Unity 手册

要参与自动布局的布局元素必须包含布局属性
布局属性主要有以下几条

  • Minmum width:该布局元素应具有的最小宽度

  • Minmum height:该布局元素应具有的最小高度

  • Preferred width:在分配额外可用宽度之前,此布局元素应具有的宽度

  • Preferred height:在分配额外可用高度之前,此布局元素应具有的高度。

  • Flexible width:此布局元素应相对于其同级而填充的额外可用宽度的相对量

  • Flexible height:此布局元素应相对于其同级而填充的额外可用高度的相对量

在进行自动布局时 都会通过计算布局元素中的这 6 个属性得到控件的大小位置

在布局时,布局元素大小设置的基本规则是

  1. 首先分配最小大小 Minmum width 和 Minmum height
  2. 如果父类容器中有足够的可用空间,则分配 Preferred width 和 Preferred height
  3. 如果上面两条分配完成后还有额外空间,则分配 Flexible width 和 Flexible height

一般情况下布局元素的这些属性都是 0
但是特定的 UI 组件依附的对象布局属性会被改变,比如 Image 和 Text

一般情况下我们不会去手动修改他们,但是如果你有这些需求
可以手动添加一个 LayoutElement 组件 可以修改这些布局属性

知识点三 水平垂直布局组件

水平布局组 (Horizontal Layout Group) - Unity 手册

垂直布局组 (Vertical Layout Group) - Unity 手册

水平垂直布局组件
将子对象并排或者竖直的放在一起

组件名:Horizontal Layout Group 和 Vertical Layout Group
参数相关:

  • Padding:左右上下边缘偏移位置

  • Spacing:子对象之间的间距

  • ChildAlignment:九宫格对其方式

  • Control Child Size:是否控制子对象的宽高

  • Use Child Scale:在设置子对象大小和布局时,是否考虑子对象的缩放

  • Child Force Expand:是否强制子对象拓展以填充额外可用空间

知识点四 网格布局组件

网格布局组 (Grid Layout Group) - Unity 手册

网格布局组件
将子对象当成一个个的格子设置他们的大小和位置

组件名:Grid Layout Group
参数相关:

  • Padding:左右上下边缘偏移位置
  • Cell Size:每个格子的大小
  • Spacing:格子间隔
  • Start Corner:第一个元素所在位置(4 个角)
  • Start Axis:沿哪个轴放置元素;Horizontal 水平放置满换行,Vertical 竖直放置满换列
  • Child Alignment:格子对其方式(9 宫格)
  • Constraint:行列约束
  • Flexible:灵活模式,根据容器大小自动适应
  • Fixed Column Count:固定列数
  • Fixed Row Count:固定行数

知识点五 内容大小适配器

内容大小适配器 (Content Size Fitter) - Unity 手册

内容大小适配器
它可以自动的调整 RectTransform 的长宽来让组件自动设置大小
一般在 Text 上使用 或者 配合其它布局组件一起使用

组件名:Content Size Fitter
参数相关

  • Horizontal Fit:如何控制宽度

  • Vertical Fit:如何控制高度

  • Unconstrained:不根据布局元素伸展

  • Min Size:根据布局元素的最小宽高度来伸展

  • Preferred Size:根据布局元素的偏好宽度来伸展宽度。

知识点六 宽高比适配器

宽高比适配器 (Aspect Ratio Fitter) - Unity 手册

宽高比适配器

  1. 让布局元素按照一定比例来调整自己的大小
  2. 使布局元素在父对象内部根据父对象大小进行适配

组件名:Aspect Ratio Fitter
参数相关:

  • Aspect Mode:适配模式,如果调整矩形大小来实施宽高比

  • None:不让矩形适应宽高比

  • Width Controls Height:根据宽度自动调整高度

  • Height Controls Width:根据高度自动调整宽度

  • Fit In Parent:自动调整宽度、高度、位置和锚点,使矩形适应父项的矩形,同时保持宽高比,会出现“黑边”

  • Envelope Parent:自动调整宽度、高度、位置和锚点,使矩形覆盖父项的整个区域,同时保持宽高比,会出现“裁剪”

  • Aspect Ratio:宽高比;宽除以高的比值

任务 40:Canvas Group 知识点

知识点一 问题:如何整体控制一个面板的淡入淡出等

如果我们想要整体控制一个面板的淡入淡出 或者 整体禁用
使用目前学习的知识点 是无法方便快捷的设置的

知识点二 解决方案:Canvas Group

为面板父对象添加 CanvasGroup 组件 即可整体控制

参数相关:

  • Alpha:整体透明度控制
  • Interactable:整体启用禁用设置
  • Blocks Raycasts:整体射线检测设置
  • Ignore Parent Groups:是否忽略父级 CanvasGroup 的作用

任务 41:知识点总结

学习主要内容

  • UGUI 基础
    • 各种控件
  • UGUI 进阶
    • UI 事件相关
    • Mask、异形按钮、自动布局
    • CanvasGroup

必须达到的水平

能够使用 UGUI 进行游戏 UI 开发

  1. 拼界面:能够数量制作各种界面
  2. 写代码:熟练利用面向对象思想编程,面板父对象脚本管理子控件逻辑
  3. 分辨率自适应:CanvasScaler 设置和 RectTransform 锚点设置

学会举一反三

UGUI 的代码是能被直接看到的

Unity 安装目录下
DataResources\PackageManager\BuiltInPackages\com.unity.ugui

随着自己的能力提升
以后可以深入去了解一下它的一些逻辑实现

每一个控件的代码相关我只讲了重要的
可以通过控件对象点出它的所有公共成员进行使用和设置

其实可以直接在 Unity 里面的 Packages 看

一些和 U 相关的知识

  1. DoTween—缓动插件,可以制作一些缓动效果
  2. TextMeshPro—文本网格插件,可以制作更多的特效文字

将在之后的课程(Unity 进阶)中进行讲解。