Unity GUI
九宫格 UI 理论
相对屏幕位置 ScreenPos:
widgetPos. x = ScreenPos + widgetCenterPos + offsetPos;
控件中心点位置 widgetCenterPos:
cw 是控件自身的宽度
ch 是控件自身的高度
最后通过操作偏移位置 offset 来摆放 UI
GUI
1 原理及作用
IMGUI (Immediate Mode Graphical User Interface)即时模式图形化交互界面
IMGUI 在 Unity 中一般简称为 GUI,它是一个代码驱动的 UI 系统
作用:
- 作为程序员的调试工具,创建游戏内调试工具
- 为脚本组件创建自定义检视面板
- 创建新的编辑器窗口和工具以拓展 Unity 本身(一般用作内置游戏工具)
- 用于进行 Unity 内置编辑器,调试工具编辑工具等等相关开发,不适合用它为玩家制作 UI 功能
GUI 的工作原理:在继承 MonoBehaviour 的脚本中的特殊函数里,调用 GUI 提供的方法,类似生命周期函数。
1 | private void OnGUI() |
- 它每帧执行相当于是用于专门绘制 GUI 界面的函数
- 一般只在其中执行 GUI 相关界面绘制和操作逻辑
- 该函数在 OnDisable 之前 LateUpdate 之后执行
- 只要是继承 Mono 的脚本都可以在 OnGUI 中绘制 GUI
缺点
- 重复工作量繁多
- 控件(widget)绘制相关代码很多
- 最大缺点: 必须运行时才能去查看结果(不能所见即所得,通过
[ExecuteAlways]
特性解决),不支持分辨率自适应
2 重要参数
[!warning]
GUI 的屏幕原点是左上角
GUI 控件绘制的共同点:
- 他们都是 GUI 公共类中提供的静态函数直接调用即可
- 他们的参数都大同小异
- 位置参数: Rect (xy 位置 wh 尺寸)
- 显示文本: string
- 图片信息: Texture
- 参数综合信息: GUIContent
- 参数自定义样式: GUIStyle
- 每一种控件都有多种重载,都是各个参数的排列组合必备的参数内容,是位置信息和显示信息
3 Label 标签
1 | public Texture texture; |
4 Button 按钮
自定义格式:
1 | public Rect rect; |
5 Toggle 开关
Toggle 意为(两种状态之间)切换
1 | //设置方法类似上面的按钮,多一个点击选中的判断,我们要自己声明一个bool值 |
排版攻略:
- 修改固定宽高 fixedwidth 和 fixedHeight
- 修改从 GUIStyle 边缘到内容起始处的空间 padding
1 | public bool isSelect; |
6 输入框和拖动条
1 | private string inputStr = ""; |
7 图片绘制和 Box 框
1 | public Rect texPos; |
简单的 Box 边框,没特殊功能
1 | GUI.Box(new Rect(0,0,100,100),"123"); |
8 工具栏和选择网格
工具栏特点,多个按钮只能同时选择一个
1 | private int toolbarIndex = 0; |
选择网格和工具栏具有相同的特点,相对 toolbar 多了一个参数 xCount,代表水平方向最多显示的按钮数量。
当 xCount 为 3 时,和上面 toolbar 绘制的 ui 一样。
当 xCount 为 2 时:
当 xCount 为 1 时:
1 | private int selGridIndex = 0; |
9 滚动视图和分组
分组
- 用于批量控制控件位置
- 可以理解为包裹着的控件加了一个父对象
- 可以通过控制分组来控制包裹控件的位置
title:GUI.BeginGroup 1
2
3
4
5
6
7
8
9
10public Rect groupPos;
private void OnGUI()
{
//批量控制控件位置
GUI.BeginGroup(groupPos);
GUI.Button(new Rect(0,0,100,50),"按钮1");
GUI.Button(new Rect(0,50,100,50),"按钮2");
GUI.EndGroup();
}
滚动视图:
1 | public Rect uiPos; |
10 窗口
就是单独的一个窗口,在绘制窗口的函数中写 UI 代码,以窗口的左上角为原点
1 | public Rect windowPos; |
模态窗口
- 可以让窗口外的其它控件无法点击
- 你可以理解该窗口在最上层,其它按钮都点击不到了,只能点击该窗口上控件
title:GUI.ModalWindow 1
GUI.ModalWindow(2,new Rect(400,100,200,150),DrawWindow,"模态窗口");
11 颜色和皮肤
1 | //全局着色,同时影响背景和字体,不常用 |
右键创建 GUI Skin,相当于 GUI style 的集合体,支持修改所有控件样式。
可以在这里面修改,通过代码传给 UI 控件。
1 | public GUISkin guiSkin; |
12 布局 GUILayout
不需要传 Rect 位置参数,自动布局,主要用于编辑器开发(编辑器 UI 排列比较整齐简单), 不适合作为游戏 UI
1 | GUILayout.BeginArea(new Rect(100,100,50,50)); //也可以使用group等统一管理位置 |
使用布局选项:
1 | GUILayout.Button("123",GUILayout.Width(300)); //布局选项作为第二个参数传入 |
13 自适应
UGUI
UGUI 是 Unity 引擎内自带的 UI 系统官方称之为: Unity Ul
是目前 Unity 商业游戏开发中使用最广泛的 UI 系统开发解决方案
它是基于 Unity 游戏对象的 UI 系统,只能用来做游戏 UI 功能,不能用于开发 Unity 编辑器中内置的用户界面
1 六大基础组件
Canvas 对象上依附的:Rect Transform
:UI 对象位置锚点控制组件,主要用于控制位置和对其方式Canvas
:画布组件,主要用于渲染 UI 控件Canvas Scaler
:画布分辨率自适应组件,主要用于分辨率自适应Graphic Raycaster
:射线事件交互组件,主要用于控制射线响应相关
EventSystem 对象上依附的:Event System
:玩家输入事件响应系统,主要用于监听玩家操作Standalone Input Module
:独立输入模块组件,主要用于监听玩家操作
Rect Transform
UI 对象位置锚点控制组件,主要用于控制位置和对其方式
Rect Transform 意思是矩形变换
是专门用于处理 UI 元素位置大小相关的组件
- RectTransform 继承于 Transform,Transform 组件只处理位置、角度、缩放
- RectTransform 在此基础上加入了矩形相关,将 UI 元素当做一个矩形来处理加入了中心点、锚点、长宽等属性,其目的是更加方便的控制其大小以及分辨率自适应中的位置适应。
@ Pivot:Pivot 轴心点默认为(0.5,0.5)
轴心点是旋转的中心(通过调节 Rotation. z 来旋转控件)
和锚点配合控制位置
@ Anchors:Anchors 轴心点默认为(0.5,0.5)
Canvas
画布组件,主要用于渲染 UI 控件
- 它是 UGUI 中所有 UI 元素能够被显示的根本
- 它主要负责渲染自己的所有 UI 子对象
- 如果 UI 控件对象不是 Canvas 的子对象,那么控件将不能被渲染
- 我们可以通过修改 Canvas 组件上的参数修改渲染方式
- 场景中可以有多个 Canvas 对象,可以分别管理不同画布的渲染方式,分辨率适应方式等等参数。如果没有特殊需求,—般情况场景上一个 Canvas 即可。
RenderMode 渲染模式
Screen Space - Overlay
覆盖模式,UI 始终显示在场景内容前方
Screen Space - Camera
摄像机模式,3D 物体可以显示在 UI 之前
- 不建议使用 Main Camera,避免场景模型遮挡 UI。
- 使用一个单独的 Camera(后文称之为 UI Camera) 负责渲染 UI。
- 主摄像机 Depth 保持默认的-1,UI Camera 的 Depth 要大于-1(深度较大的绘制在深度较小的上方)
- 主摄像机 Culling Mask 取消勾选 UI
- UI Camera 的 Culling Mask 只选择 UI,Clear Flags设置为 Depth Only(只画该层,背景透明,这样才不会让 UI 遮挡后面的内容)
- 如果需要在 UI 上显示 3D 模型,直接在 Canvas 上创建即可,Layer 要设置成 UI
- 通过设置 Sorting Layer,也可以对 Canvas 进行排序,后面的层覆盖前面的层。
- Order in Layer,适用于相同 Layer 中进行排序
Screen Space - Camera
3D 模式,可以把 UI 对象像 3D 物体一样处理,常用于 VR 或者 AR
Event Camera:用于处理 UI 事件的摄像机( 如果不设置,不能正常注册 UI 事件)
Canvas Scaler
**画布缩放控制器,用于画布分辨率自适应的组件
它主要负责在不同分辨率下 UI 控件大小自适应
它并不负责位置,位置由之后的 Rect Transform 组件负责
提供了三种用于分辨率自适应的模式(按需选择)
Constant Pixel Size(恒定像素模式)∶
无论屏幕大小如何,U 始终保持相同像素大小Scale With Screen Size (随屏幕尺寸缩放模式)∶
根据屏幕尺寸进行缩放,随着屏幕尺寸放大缩小Constant Physical Size(恒定物理模式):
无论屏幕大小和分辨率如何,UI 元素始终保持相同物理大小
分辨率
屏幕分辨率——当前设备的分辨率,编辑器下 Game 窗口中 Stats 可以查看到
参考分辨率 Reference Resolution——在 Scale With Screen Size 缩放模式中出现的关键参数,参与分辨率自适应的计算
画布宽高和缩放系数——分辨率自适应会改变的参数,通过屏幕分辨率和参考分辨率计算而来。选中 Canvas 对象后在 Rect Transform 组件中看到的宽高和缩放系数
1
2
3//分辨率为(x,y),则:
Width * Scale. x = 分辨率x
Height * Scale. y = 分辨率y分辨率大小自适应——通过一定的算法以屏幕分辨率和参考分辨率参与计算得出缩放系数该结果会影响所有 UI 控件的缩放大小
UI Scale Mode UI 缩放模式
重点:
Constant Pixel Size 恒定像素模式
无论屏幕大小如何,U 始终保持相同像素大小
它不会让 UI 控件进行分辨率大小自适应
会让 UI 控件始终保持设置的尺寸大小显示
一般在进行游戏开发极少使用这种模式,除非通过代码计算来设置缩放系数
- Scale Factor: 缩放系数,按此系数缩放画布中的所有 UI 元素
- Reference Pixels Per Unit:单位参考像素,多少像素对应 Unity 中的一个单位(默认一个单位为 100 像素),图片设置中的 Pixels Per Unit 设置,会和该参数一起参与计算
Set Native Size:恢复 Source Image 的原始尺寸,结果需要经过计算:
Scale With Screen Size 随屏幕尺寸缩放模式
根据屏幕尺寸进行缩放,随着屏幕尺寸放大缩小,最常用
Reference Resolution :参考分辨率 (PC 常用 1920x1080,手机端也要适配对应分辨率,一般由美术人员决定)。缩放模式下的所有匹配模式都会基于参考分辨率进行自适应计算
Screen Match Mode:屏幕匹配模式,当前屏幕分辨率宽高比不适应参考分辨率时,用于分辨率大小自适应的匹配模式。
- 有三种模式:最常使用的是 Match Width Or Height 模式,套路如下:
- 有三种模式:最常使用的是 Match Width Or Height 模式,套路如下:
三种模式的详细解释
- Expand: 水平或垂直拓展画布区域,会根据宽高比的变化来放大缩小画布,可能有黑边:
- Shrink: 水平或垂直裁剪画布区域,会根据宽高比的变化来放大缩小画布,可能会裁剪
- Match Width Or Height: 以宽高或者二者的平均值作为参考来缩放画布区域(常用)
- Expand: 水平或垂直拓展画布区域,会根据宽高比的变化来放大缩小画布,可能有黑边:
Constant Physical Size 恒定物理模式
无论屏幕大小和分辨率如何,UI 元素始终保持相同物理大小
DPI: (Dots Per Inch,每英寸点数)图像每英寸长度内的像素点数
Physical Unit:物理单位,使用的物理单位种类
Falback Screen DPI:备用 DPI,当找不到设备 DPI 时,使用此值 Default Sprite DPI: 默认图片 DPI
World 世界模式
Graphic Raycaster
Graphic Raycaster 意思是图形射线投射器(不是基于碰撞器,而是基于图形)
- 用于检测 UI 输入事件
- 主要负责通过射线检测玩家和 UI 元素的交互,判断是否点击到了 UI 元素
lgnore Reversed Graphics: 是否忽略反转图形
- ? 反转指的是将控件的 Rect Transfrom 中的 Rotation 属性的 x 或 y 轴旋转 180 度
Blocking Objects: 射线被哪些类型的碰撞器阻挡 (在覆盖渲染模式 Screen Space - Overlay 下无效)
Blocking Mask: 射线被哪些层级的碰撞器阻挡(在覆盖渲染模式下无效)
演示:
在一个 Button 控件前分别放一个 3D object(Cube) 和 2D object(Sprite),这两个 object 都要添加碰撞器,如下:
- 当 Blocking Objects 为 None 时,可以点击到 button
- 当 Blocking Objects 为 2D 时,右边点不到 button
- 当 Blocking Objects 为 3D 时,左边点不到 button
- 当 Blocking Objects 为 all 时,两边都点不到 button
Event System
Event System 意思是事件系统
玩家输入事件响应系统,主要用于监听玩家操作
- 它是用于管理玩家的输入事件并分发给各 UI 控件
- 它是事件逻辑处理模块,所有的 UI 事件都通过 EventSystem 组件中轮询检测并做相应的执行
- 它类似一个中转站,和许多模块一起共同协作,如果没有它,所有点击、拖曳等等行为都不会被响应
First Selected
: 首先选择的游戏对象,可以设置游戏一开始的默认选择Send Navigation Events
: 是否允许导航事件(开启后可以通过键盘控制移动/按下/取消,wasd 移动,空格/回车选择)Drag Threshold
: 拖拽操作的阈值(移动多少像素的距离才算开始拖拽)
Standalone Input Module
独立输入模块组件,主要用于监听玩家操作
- 它主要针对处理鼠标/键盘/控制器/触屏的输入
- 输入的事件通过 Event System 进行分发
- 它依赖于 Event System 组件,他们两缺一不可
和 Input Manager 中的设置绑定,一般不会进行修改:Horizontal Axis
: 水平轴按钮对应的热键名 (该名字对应 Input 管理器)Vertical Axis
: 垂直轴按钮对应的热键名(该名字对应 Input 管理器)Submit Button
: 提交(确定)按钮对应的热建名(该名字对应 Input 管理器)Cancel Button
: 取消按钮对应的热建名 (该名字对应 Input 管理器)
Input Actions Per Second
: 每秒允许键盘/控制器输入的数量Repeat Delay
: 每秒输入操作重复率生效前的延迟时间ForceModule Active
: 是否强制模块处于激活状态
代码获取组件属性
1 | //因为Transform是RectTransform父类,所以可以强转为RectTransform |
2 三大基础控件
Image
是 UGUI 中用于显示精灵图片(Sprite)的关键组件
除了背景图等大图用 RawImage,一般都使用 Image 来显示 UI 中的图片元素
@ 控件显示顺序:根据在 Canvas 下的层级,越后面的优先级越高:
@ Raycast Taget 示例:在 Button 控件前加一个 Image 控件
- 默认勾选,重叠部分无法点击 Button
- 取消勾选,重叠部分可以点击 Button
@ ImageType 图片类型
- Simple:只用于固定尺寸的图片,美术出什么尺寸就用什么尺寸
- Sliced 切片模式:拉伸常用,需要设置图片边框 border
- Tiled-平铺模式:重复平铺中央部分,可以设置图片边框 border
- Filled-填充模式:效果较多,可以做血条 cd 等效果
设置图片边框 border 的步骤:
- 找到图片,点击 SpriteEditor:
- 拉动绿色线,将图片分割成九宫格区域。当拉伸图片时,横向拉伸只会拉伸竖向的中间一排,竖向拉伸只会拉伸横向的中间一排。四角不会发生拉伸
代码获取 Image 属性
1 | //修改当前Image控件的SourceImage |
Text
有两个版本
- Text (TMP),基于 TextMeshPro
- Text (Legacy),旧版
Text (TMP)
1 | public TMP_Text text; //声明 |
Text (Legacy)
代码控制文本内容
1 | //Text(TMP) |
RawImage
RawImage 是原始图像组件
是 UGUI 中用于显示任何纹理图片的关键组件
和 Image 的区别:
- 一般 RawImage **用于显示大图 (背景图、不需要打入图集的图片、网络下载的图等等)**。Image 则用于显示一些小的 UI 元素。
- RawImage 支持各种 Texture Type,Image 必须使用 Sprite
代码控制 Texture
1 | RawImage img = this.GetComponent<RawImage>(); |
3 组合控件
Button 按钮
按钮组件
是 UGUI 中用于处理玩家按钮相关交互的关键组件
- Navigation 要联动 Event System:
- Explicit 指定周边控件:
- 导航连线:
代码控制 button 属性
1 | Button btn = this.GetComponent<Button>(); |
监听点击事件
点击事件是在按钮区域按下抬起一次就算一次点击
监听点击事件有两种方式:
拖拽对象
只显示脚本上的 public 方法代码添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23void Start()
{
Button button = GetComponent<Button>();
//添加监听,原理就是委托
button.onClick.AddListener(ClickButton);
//也可以使用lambda表达式
button.onClick.AddListener(() =>
{
print("另一种方式ClickButton");
});
//移除监听
button.onClick.RemoveListener(ClickButton);
//移除所有监听
button.onClick.RemoveAllListeners();
}
public void ClickButton()
{
print("ClickButton");
}
异形按钮
异形即形状不规则
普通的 Button 是根据矩形区域来响应点击,当我们使用带有透明部分的图片时,如图,点击透明区域也会响应,我们只想要不透明部分作为 button
方法一:添加子对象
按钮之所以能够响应点击,主要是根据图片矩形范围进行判断的
它的范围判断是自下而上的,意思是如果有子对象 Button,点击子对象 Button 的矩形范围也会让上面的 button 响应,那么我们就可运用多个透明图拼凑不规则图形作为按钮,子对象用于进行射线检测
如下图,先用一个 Image 作为背景图,然后修改各个 Button 按钮的矩形范围,拼出大致区域即可。
方法二:通过代码改变图片的透明度响应阈值
- 第一步: 修改图片参数开启 Read/ write Enabled 开关,会增大内存消耗
- 第二步: 通过代码修改图片的响应阈值
1 | public Image image; |
Toggle 开关
开关组件
是 UGUI 中用于处理玩家单选框多选框相关交互的关键组件
开关组件默认是多选框
可以通过配合 ToggleGroup 组件制作为单选框(单选框就是多个框只能同时选择其中的一个)
- canvas 下创建一个空 object 命名为 GroupObject,添加 ToggleGroup 组件,然后将多个 Toggle 作为其子对象(Allow Switch Off 即是否允许所有选项都为关闭状态)
![]()
- 每个 Toggle 的 Group 都设置为 GroupObject
默认创建的 Toggle 由 4 个对象组成
- 父对象:Toggle 组件依附
- 子对象:背景图 (必备)、选中图 (必备)、说明文字 (可选)
Interactable、Transition、Navigation 设置和 Button 一致
代码控制
1 | Toggle toggle = this.GetComponent<Toggle>(); |
监听点击事件
点击事件是在按钮区域按下抬起一次就算一次点击
监听点击事件有两种方式:
拖拽对象,注意选择的函数必须有 bool 形参,表示打开和关闭
代码添加
1
2
3
4
5
6
7
8
9
10
11void Update()
{
Toggle toggle = GetComponent<Toggle>();
tog.onValueChanged.AddListener(ChangeValue);
}
//必须传bool形参
public void ChangeValue(bool isOn)
{
print("状态改变" + isOn);
}
InputField 输入字段
输入字段组件
是 UGUI 中用于处理玩家文本输入相关交互的关键组件
默认创建的 InputField 由 3 个对象组成
父对象:InputField 组件依附对象,以及同时在其上挂载了一个 Image 作为背景图
子对象:文本显示组件 (必备)、默认显示文本组件 (必备)
InputField (TMP)
1 | public TMP_InputField inputField; //声明 |
InputField (Legacy)
Content Type:
1 | InputField input = this.GetComponent<InputField>();print(input.text); |
Slider 滑动条
滑动条组件
是 UGUI 中用于处理滑动条相关交互的关键组件
默认创建的 Slider 由 4 组对象组成
父对象:Slider 组件依附的对象
子对象:背景图、进度图、滑动块三组对象
1 | Slider slider = GetComponent<Slider>(); |
Scrollbar 滚动条
滚动条组件
是 UGUI 中用于处理滚动条相关交互的关键组件
默认创建的 scrollbar 由 2 组对象组成
父对象:Scrollbar 组件依附的对象子对象
滚动块对象:一般情况下我们不会单独使用滚动条,都是配合 ScrollView 滚动视图来使用
ScrollView 滚动视图
滚动视图组件
是 UGUI 中用于处理滚动视图相关交互的关键组件
默认创建的 ScrollRect 由 4 组对象组成
父对象:ScrollRect 组件依附的对象,还有一个 Image 组件最为背景图
子对象:
Viewport 控制滚动视图可视范围和 Content 控制内容范围 (内部控件都放在 Content 下面 )
Scrollbar Horizontal 水平滚动条
Scrollbar Vertical 垂直滚动条
DrawDown 下拉列表
下拉列表(下拉选单)组件
是 UGUI 中用于处理下拉列表相关交互的关键组件
默认创建的 DropDown 由 4 组对象组成
父对象:DropDown 组件依附的对象还有一个 Image 组件作为背景图
子对象:
Label 是当前选项描述
Arrow 右侧小箭头
Template 下拉列表选单
DrawDown (TMP)
DrawDown (Legacy)
1 | Dropdown dropdown = GetComponent<Dropdown>(); |
4 自动布局组件
虽然 UGUI 的 RectTransform 已经非常方便的可以帮助我们快速布局,但 UGUI 中还提供了很多可以帮助我们对 UI 控件进行自动布局的组件,他们可以帮助我们自动的设置 UI 控件的位置和大小等
自动布局的工作方式:自动布局控制组件 + 布局元素 = 自动布局
自动布局控制组件:unity 提供了很多用于自动布局的管理性质的组件用于布局
布局元素: 具备布局属性的对象们,这里主要是指具备 RectTransform 的 UI 组件
[!quote] 布局属性
要参与自动布局的布局元素必须包含布局属性,布局属性主要有以下几条Minmum width
: 该布局元素应具有的最小宽度Minmum height
: 该布局元素应具有的最小高度
Preferred width
: 在分配额外可用宽度之前,此布局元素应具有的宽度Preferred height
: 在分配额外可用高度之前,此布局元素应具有的高度。
Flexible width
: 此布局元素应相对于其同级而填充的额外可用宽度的相对量Flexible height
: 此布局元素应相对于其同级而填充的额外可用高度的相对量在进行自动布局时都会通过计算布局元素中的这 6 个属性得到控件的大小位置
- ! 一般情况下我们不会去手动修改他们,但是如果你有这些需求,可以手动添加一个
LayoutElement
组件,可以修改这些布局属性。控件 Insepctor 最下方可以查看布局属性
在布局时,布局元素大小设置的基本规则:
- 首先分配最小大小
Minmum width
和Minmum height
- 如果父类容器中有足够的可用空间,则分配
Preferred width
和Preferred height
- 如果上面两条分配完成后还有额外空间,则分配
Flexible width
和Flexible height
一般情况下布局元素的这些属性都是 0,但是特定的 UI 组件依附的对象布局属性会被改变,比如 Image 和 Text
水平垂直组件
组件名:Horizontal Layout Group 和 Vertical Layout Group
将子对象并排或者竖直的放在一起
通常将组件给父对象,那么子对象就会自动布局,如图,红色 Image 作为父对象,其他颜色 Image 作为子对象,父对象添加 Horizontal Layout Group 组件:
参数相关:
Padding: 左右上下边缘偏移位置
Spacing: 子对象之间的间距
ChildAlignment: 九宫格对其方式
Control Child size: 是否控制子对象的宽高
Use child Scale: 在设置子对象大小和布局时,是否考虑子对象的缩放
child Force Expand: 是否强制子对象拓展以填充额外可用空间
网格布局组件
组件名: 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
它可以自动的调整 RectTransform 的长宽来让组件自动设置大小
一般在 Text 上使用或者配合其它布局组件一起使用
参数相关
Horizontal Fit: 如何控制宽度
Vertical Fit: 如何控制高度
Unconstrained: 不根据布局元素伸展
Min size: 根据布局元素的最小宽高度来伸展
Preferred Size: 根据布局元素的偏好宽度来伸展宽度。
常用情景,背包动态扩容
为 Content 添加一个网格布局组件,然后不断添加 Image ,我们发现,随着 Image 数量增多,Content 的 Rect 高度并没有增加,这就导致,滚轮无法查看所有格子:
我们只需为 Content 添加内容大小适配器,将 Verticla Fit 设置为 Preferred Size,就可以了:
宽高比适配器
组件名: Aspect Ratio Fitter
让布局元素按照一定比例来调整自己的大小,使布局元素在父对象内部根据父对象大小进行适配
参数相关:
Aspect Mode: 适配模式, 如果调整矩形大小来实施宽高比
None: 不让矩形适应宽高比
width Controls Height: 根据宽度自动调整高度
Height Controls width: 根据高度自动调整宽度
Fit In Parent: 自动调整宽度、高度、位置和锚点,使矩形适应父项的矩形,同时保持宽高比,会出现黑边
Envelope Parent: 自动调整宽度、高度、位置和锚点,使矩形覆盖父项的整个区域,同时保持宽高比,会出现“裁剪
Aspect Ratio: 宽高比; 宽除以高的比值
5 图集 (需要补一下 Unity 核心)
UGUI 和 NGUI 使用上最大的不同是:NGUI 使用前就要打图集,UGUI 可以再之后再打图集
打图集的目的就是减少 Drawcall 提高性能,我们可以通过打图集,将小图合并成大图,将本应 n 次的 Drawcall 变成 1 次 Drawcall 来提高性能。
Sprite Packer
Sprite Packer (精灵包装器,可以通过 Unity 自带图集工具生成图集)
Edit->Project Setting->Editor
- Disabled: 默认设置, 不会打包图集
- Enabled For Build(常用): Unity 仅在构建时打包图集,在编辑器模式下不会打包
- Always Enabled(常用): Unity 在构建时打包图集,在编辑模式下运行前会打包图集
图集参数
创建图集:create->2D->Sprite Atlas
图集打包后,不要让外部部件插入其中,这样会增加 drawcall
1 | //加载图集注意:需要引用命名空间 |
6 UI 事件接口
目前所有的控件都只提供了常用的事件监听列表
如果想做一些类似长按,双击,拖拽等功能是无法制作的,或者想让 Image 和 Text, RawImage 三大基础控件能够响应玩家输入也是无法制作的
而事件接口就是用来处理类似问题,让所有控件都能够添加更多的事件监听来处理对应的逻辑
常用事件接口:IPointerEnterHandler
- OnPointerEnter
-当指针进入对象时调用 (鼠标进入)IPointerExitHandler
- OnPointerExit
-当指针退出对象时调用 (鼠标离开)IPointerDownHandler
- OnPointerDown
-在对象上按下指针时调用 (按下)IPointerUpHandler
- OnPointerUp
-松开指针时调用(在指针正在点击的游戏对象上调用)(抬起)IPointerClickHandler
- OnPointerclick
-在同一对象上按下再松开指针时调用 (点击)
IBeginDragHandler
- OnBeginDrag
-即将开始拖动时在拖动对象上调用 (开始拖拽)IDragHandler
- OnDrag
- 发生拖动时在拖动对象上调用 (拖拽中)IEndDragHandler
- OnEndDrag
-拖动完成时在拖动对象上调用 (结束拖拽)
使用方法
- 继承 MonoBehavior 的脚本继承对应的事件接口,引用命名空间
- 实现接口中的内容
- 将该脚本挂载到想要监听自定义事件的 UI 控件上
1
2
3
4
5
6
7
8
9
10
11
12
13
14//需要什么接口就继承什么
public class Test : MonoBehaviour, IPointerEnterHandler, IPointerClickHandler
{
//实现接口内容
public void OnPointerEnter(PointerEventData eventData)
{
print("鼠标进入");
}
public void OnPointerClick(PointerEventData eventData)
{
print("鼠标点击");
}
}
PointerEventData
上面实现的接口内容都有一个 PointerEventData
类型的参数
父类: BaseEventData
pointerId
: 鼠标左右中键点击鼠标的 ID ,通过它可以判断左中右键点击,对一个 ID 分别为-1,-2,-3position
: 当前指针位置 (屏幕坐标系)pressPosition
: 按下的时候指针的位置 delta: 指针移动增量clickCount
: 连击次数 clickTime: 点击时间pressEventCamera
: 最后一个 onPointerPress
按下事件关联的摄像机enterEvetnCamera
: 最后一个 onPointerEnter
进入事件关联的摄像机
1 | public void OnPointerClick(PointerEventData eventData) |
EventTrigger
事件触发器是 EventTrigger 组件
它是一个集成了上节课中学习的所有事件接口的脚本,它可以让我们更方便的为控件添加事件监听
直接在 UI 控件上添加 EventTrigger 即可:
使用方法:
- 直接拖脚本关联,注意传入的函数参数为 BaseEventData 类型
1 | public void TestPointerEnter(BaseEventData eventData) |
- 代码关联
1
2
3
4
5
6
7
8
9
10
11//声明事件
EventTrigger.Entry entry = new EventTrigger.Entry();
//设置事件类型
entry.eventID = EventTriggerType.PointerEnter;
//设置回调函数
entry.callback.AddListener((data)=>{ Debug.Log("鼠标进入"); });
//添加事件
eventTrigger.triggers.Add(entry);
7 屏幕坐标转 UI 坐标
RectTransformUtility
公共类是一个 RectTransform
的辅助类主要用于进行一些坐标的转换等等操作,其中对于我们目前来说最重要的函数是将屏幕空间上的点,转换成 UI 本地坐标下的点
方法:RectTransformUtility. ScreenPointToLocalPointInRectangle
参数一:相对父对象
参数二:屏幕点
参数三:摄像机
参数四:最终得到的点
一般配合拖拽事件使用
将以下脚本挂在给子对象,可以通过鼠标拖动 image 子对象相对于父对象移动
1 | public class Test : MonoBehaviour , IDragHandler |
9 Mask 遮罩
实现遮罩效果的关键组件时 Mask 组件
通过在父对象上添加 Mask 组件即可遮罩其子对象
注意:
- 想要被遮罩的 Image 需要勾选 Maskable
- 只要父对象添加了 Mask 组件,那么所有的 UI 子对象都会被遮罩
- 遮罩父对象图片的制作,不透明的地方显示,透明的地方被遮罩
遮罩前:
使用遮罩:
10 模型和粒子显示在 UI 之前
方法一:直接用摄像机渲染 3D 物体
Canvas 的渲染模式:摄像机模式和世界 (3D)模式都可以让模型显示在 UI 之前((Z 轴在 UI 元素之前即可)
注意:
- 摄像机模式时建议用专门的摄像机渲染 UI 相关
- 面板上的 3D 物体建议也用 UI 摄像机进行渲染
![[#Screen Space - Camera]]
方法二:渲染在 RT 上,通过 RawImage 显示
专门使用一个摄像机渲染 3D 模型,将其渲染内容输出到 Render Texture 上,类似小地图的制作方式
再将渲染的图显示在 UI 上
该方式不管 canvas 的渲染模式是哪种都可以使用
- 创建一个专用摄像机,将想要渲染的模型单独设置一个 Layer,将摄像机的 CullingMask 设置为该 Layer,之渲染该模型
- Create->RenderTexture,将创建的 RT 传给摄像机
- Canvas 下创建一个 RawImage(RawImage 支持各种 Texture Type),将 RT 传过去就可以了。
方法三:粒子系统 Order in Layer
粒子系统也可以使用方法一和方法二,同时有一个单独的方法:
canvas 和粒子系统都有一个层级排序选项,通过修改粒子系统的序号,让值大于 Canvas,即可实现忽略 z 轴,粒子始终显示在 UI 前
11 CanvasGroup
为面板父对象添加 CanvasGroup 组件即可同时控制一组 Canvas
常用于整体控制一个面板的淡入淡出或者整体禁用
参数相关:
Alpha: 整体透明度控制
Interactable: 整体启用禁用设置
Blocks Raycasts: 整体射线检测设置
Ignore Parent Groups: 是否忽略父级 CanvasGroup 的作用
12 常用插件
DoTween—缓动插件,可以制作一些缓动效果
TextMeshPro: 一文本网格插件,可以制作更多的特效文字
13 实战
ShaderGUI
ShaderGUI 分为两种,一种是 Drawer 一种是 GUI,当然可以把 GUI 集成后用 Drawer 的形式写。
这里主要说的是 GUI,因为 Drawer 有很多限制,例如修改 RenderType 的时候就很麻烦。
回到 GUI。大部分 GUI 继承自两个类,**ShaderGUI & BaseShaderGUI
**(可以去 Unity 里翻翻,记得改成 all)。前者是自己造轮子,后者是根据前者的基础上造好了一些轮子(如果是在 Lit 基础上魔改的,用 BaseShaderGUI 方便一些)这里主要是抄作业
另外一点就是参考默认 lit. shader 的时候会发现CustomEditor里面是一个namespace,所以其实也可以写成CustomEditor “namespace. name”的形式
再有一点,Drawer 和 GUI 是分开的独立的,互不影响。如果需要影响的话,有一个 base. OnGUI 可以达到 Drawer 和 GUI 混用
一、Drawer
Unity 为用户提供了基础类:MaterialPropertyDrawer
,专门用于快速实现自定义材质面板的目的。
常用的属性特性
[Space]
单行空格[Space (5)]
五行空格[Header (name)]
标题名[HideInInSpector]
:在 InSpector 面板隐藏[NoScaleOffset]
:隐藏纹理的 Tiling 和 Offset[Normal]
:检测是否为 NormalMap[HDR]
:指示纹理或颜色属性使用高动态范围 (HDR) 值。[Gamma]
:指示浮点数或矢量属性使用 sRGB 值
![[1 ShaderLab#颜色空间和颜色/矢量着色器数据]]
[MainTexture]
:将纹理设置为主纹理,默认情况 Unity 会将属性名为 _MainTex
的纹理设置为主纹理。如果 Shader 中有多个该命令,只有第一个命令会生效
1 | public Texture texture; |
[MainColor]
:将属性设置为主颜色,默认情况 Unity 会将名为_Color
的纹理设置为主纹理。如果 Shader 中有多个该命令,只有第一个命令会生效title:脚本访问主颜色 1
2
3
4
5
6public Texture texture;
void Start()
{
Material.mat = GetConponent<Renderer>().material;
mat.color = color.red;
}
[PerRendererData]
:指示纹理属性将来自每渲染器数据,形式为 MaterialPropertyBlock。材质 Inspector 会将这些属性显示为只读。
不同类型的 DrawerClass
在编写 Shader 的时候,DrawerClass 需要写在对应属性之前的“[]
”中,类别的后缀名称“Drawer”不需要添加,因为 Unity 在编辑的时候会自动添加。
IntRange(整数滑动条)
[IntRange]_Alpha("Alpha",Range(0,255)) = 0
Space(垂直间隔)
[Space]_Prop1("Prop1",Float) = 0
也可以加数字增大间隔[Space(50)]_Prop2("Prop2",Float) = 0
Header(标题头)
[Header(Title)]_Title("Title",Float) = 0
PowerSlider(指数式的滑动条)
[PowerSlider(3.0)]_Shininess("Shininess",Range(0,1)) = 0
Enum(枚举)
[Enum(Zero,0,One,1,Two,2,Three,3)] _Number ("Number", Float) = 0
KeywordEnum(枚举)
[KeywordEnum(None,Add,Multiply)]_Overlay("OverLay Mode",Float) = 0
KeywordEnum 和 Enum 使用上有些不同,区别在于 KeywordEnum 类似于 if-else,同时在 shader 代码中需要处理
Toggle 和 ToggleOff
将 float 类型的数据以开关的形式在材质属性面板上显示,数值只能设置为 0 或 1,0 为关闭,1 为开启。
当 Toggle 开启,Shader 关键词会被 Unity 默认设置为 property name_ON
当 ToggleOff 开启,Shader 关键词会被 Unity 默认设置为 property name_OFF
注意:关键词的所有字母必须大写。
1 | //1、声明Property,格式为:[ToggleOff] VarName("Display", Int) = 0/1 |
除了使用 Unity 默认的关键词,也可以自定义一个特殊的关键词,例如:
1 | [Toggle (ENABLE_FANCY)] _Fancy ( "Fancy? " ,Float) = 0 |
括号内的名称 ENABLE_FANCY 即为自定义的 Shader 关键词。
Enum
枚举(Enum)将 float 类型的数据以下拉列表的形式在材质属性面板上显示,Unity 为用户提供了一些内置的枚举类,例如 BlendMode、CillMode、CompareFunction,举个例子:
1 | Properties |
这是 Unity 内置的所有混合系数的枚举类,默认值为 0 表示选择第一个混合系数,默认值为 1 表示选择第二个混合系数,
以此类推。最终在材质面板上的显示效果如图 11-1 所示,这些选项就是 Shader 中可以使用的所有混合系数。
当然,用户也可以自己定义枚举的名称/数值对,但是一个枚举最多只能自定义 7 个名称/数值对。举个例子:
1 | ["ZWrite", Float) = 0 ] _Zwrite ( |
上述例子定义的枚举为“是否深度写入”,括号内为定义的名称/数值对,序号 0 对应 Off,序号 1 对应 On,中间用符号“,”间隔开。默认为序号 0,也就是 Off。
KeywordEnum
关键词枚举(KeywordEnum)跟普通的枚举类似,也是将 float 类型的数据以下拉列表的形式在材质属性面板上显示,只不过关键词枚举会有与之对应的 Shader 关键词,在 Shader 中通过 #pragma shader_feature
或 #pragma multi_compile
指令可以开启或者关闭某一部分 Shader 代码。
Shader 关键词格式为:property name_enum name
,属性名称+“下画线”+枚举名称,所有英文必须大写,并且最多支持 9 个关键词。举个例子:
1 | [KeywordEnum(None,Add,Multiply)] _Overlay("Overlay mode", Float) = 0 |
括号内的 None,Add,Multiply 是定义的 3 个枚举名称,中间用逗号隔开。默认值为 0,表示默认使用 None。这三个选项所对应的 Shader 关键词分别为:_OVERLAY_NONE
、_OVERLAY_ADD
和 _OVERLAY_MULTIPLY
。
定义如下:
使用如下:
在编译指令中定义关键词
定义了 ToggleDrawer
或者 KeywordEnumDrawer
之后,如果想要正常使用,还需要在编译指令中声明 Shader 关键词。例如,上面定义的 None、Add、Multiply 关键词枚举,在编译指令中的代码如下:
1 |
不同关键词之间需要用空格间隔开。
另外,也可以使用另一种编译指令定义关键词,代码如下:
1 |
虽然表面上看似通过一个 Shader 文件实现了不同种情况,但是 Unity 会自动将不同情况编译成不同版本的 Shader 文件,这些不同版本的 Shader 文件被称为 Shader 变体(Variants),上述编译指令中包含三个 Shader 变体。
假设再添加一个指令:
1 |
本指令包含 Toggle 的关闭与开启两种情况,所以 Unity 最终会编译出 2×3=6 个 Shader 变体。因此在使用大量 shader feature 或 multi compile 指令的时候,无形之中会产生大量的 Shader 变体文件。
两种不同编译指令之间的区别如下:
(1)shader_feature:只会为材质使用到的关键词生成变体,没有使用到的关键词不会生成变体,因此无法在运行的时候通过脚本切换效果。
(2)multi_compile:会为所有关键词生成变体,因此可以在运行的时候通过脚本切换效果。
在 Shader 文件的属性设置面板中可以查看到本 Shader 生成的变体数量,如图 11-2 所示,通过开启“Skip unused shader_features”选项可以只查看使用关键词的变体数量,也可以关闭“Skip unused shader_features”选项查看所有关键词的变体数量。如果需要确定具体的关键词是哪些,可以单击“Show”查看。
内置枚举 UI 汇总
ZWriteMode 是没有内置的,实际上也只有 on 和 off 两个状态,所以用 Toogle 其实也可以,这里是直接用 [Enum(Off, 0, On, 1)]
这样的写法声明了个新的自定义 Enum
想要知道 unity 还有哪些 shader 里可以用的 Attributes 可以看看 MaterialPropertyDrawer. cs 这个文件,或者继承 MaterialPropertyDrawer 后自己写一个。
1 | public enum CustomEnum |
1 | Shader "Mya/EnumTest" |
1 | using System; |
二、CustomEditor GUI
待补充…
我们使用 CustomEditor
来扩展材质面板,声明在 Shader 最下方。
CustomEditor
:可为着色器定义一个 CustomEditor。如果执行了此操作,Unity 将查找具有此名称并能扩展 ShaderGUI 的类。如果找到,则使用此着色器的所有材质都将使用此 ShaderGUI
我们要在 Editor
文件夹创建一个指定的 CustomShaderGUI.cs
脚本
1 | CustomEditor "CustomShaderGUI.cs" |
该类继承 ShaderGUI
并重载 OnGUI
方法来扩展材质编辑器。
ShaderGUI 通过 shader 中的 CustonEditor 关联 UI 脚本,Unity 会调用 OnGUI 来绘制面板, UI 脚本必须放入 Editor 文件夹中.
1 | public class TestGUI : ShaderGUI |
我们开启自定义 UI 显示以后,默认 UI 将会失效。
贴图单行显示
FindProperty
会根据属性名称 ID 去查找 material Properties 中包含的相应属性,Content 是一个显示 Lable, 它包含了属性名称,属性数值和属性 Tips。最后用 materialEditor 绘制单行贴图 UI
参数一:propertyname 材质属性的名称
参数二:properties 可用材质属性的数组
参数三:propertylsmandatory 如果为 true,则如果没有找到 propertyName 属性,此方法将抛出异常。
1 | //显示贴图 |
添加调色给这张图, 在原有属性下方查找到颜色,GUI 容器还是使用 cubemap 的, 这时 color 就会出现在 cubemap 之后单行显示。
1 | MaterialProperty tint = FindProperty (“_Color”, materialProperties, true); |
法线单行显示,无贴图隐藏滑竿
1 | MaterialProperty _Normal = FindProperty(“Normal”, materialProperties,true); |
贴图特殊设置提示
在默认 attribute 中我们使用法线贴图时会提示我们当前传入图片是否是法线,我们可以借鉴这一功能定义我们自己需要设置的内容作为提示显示出来。
1 | MaterialProperty _Tex2 = FindProperty("_Tex2", properties); |
1 | //当我们修改状态以后可以对离线资源进行同步设置 |
UI 界面变更检查
现在是在 OnGUI 中每帧重复执行所有方法 (并不是每帧重绘制),我们应当是 material 属性改变以后在执行内部方法赋值 shader。
1 | void SetKeyWord(string keyword, bool enable) |
1 | //也可以根据 debug 来查看 keyword 是否成功 |
根据条件隐藏显示所属 UI 控件
1 | //设置列表显示关键字 |
我们将 enum 控件绘制在最顶端,根据修改去赋值 shader
1 | EditorGUI.BeginChangeCheck(); |
因为我们设置了分支关键字,shader 内会根据关键字走相应流程。我们可以将不被使用的流程属性隐藏。
折叠组
和上一步的实现类似区别在于使用一个带有折叠判断的控件绘制, 可以使用 FoldoutHeaderGroup 或 Foldut
1 | isFoldut = EditorGUILayout.BeginFoldoutHeaderGroup(isFoldut, "Group 01"); |
可调节 min max 的滑动条
节省控件位置或者更直观的表达时使用,中间以 0 为例,左区间是 [minLimit, 0] 右[0, maxLimit]
左右区间是可以被动态修改的
1 | EditorGUILayout.MinMaxSlider(ref minVal, ref maxVal, minLimit, maxLimit); |
控件容器 Rect
在界面中每一个控件都可以定制长宽。x,y 0 点在左上角
如果我们手动设置 rect,那样以后排板将会很痛苦。
1 | //获取上一个Rect |
判断执行事件是为了避免获取失效。
我们用一个 silder 来控制一个 box 的长短,使其 100% 时填充满 inspector 宽。
1 | slider = EditorGUILayout.Slider(slider, 0, 1); |