前言
此文延续上篇文章Unity UGUI源码解析,来进行对应的关于UGUI布局这块的讲解,关于布局刷新这块逻辑请参考上文中Canvas渲染重绘流程,用样适用布局更新这块逻辑,由于组件都是功能向的脚本,所以此文主要说明布局组件的使用方法,不对代码有过多的深入,具体实现逻辑请查看源码。
UGUI布局组件详解
组件概览
让我们先看一下UGUI框架中在UI布局这方面有哪些脚本,再来进行一一讲解
可以看到所有带icon图片的脚本都是我们可以在对UI进行布局的时候进行使用的,这里核心的脚本组合是LayoutGroup
和ILayoutElement
,其他脚本都是继承他实现了如竖向,横向,网格排列。
- LayoutGroup 主要指定了布局排列的方式
- ILayoutElement 提供了单个布局元素的信息,将覆盖原始布局信息
画布自适应
首先让我们思考一下为什么需要画布自适应吧,假使在没有画布自适应的情况下,我们会因为无法确定屏幕物理分辨率还有屏幕宽高比而无法确认我们的图片资源应该要画多大,以及我们组件需要摆放的位置还有渲染大小都无法确定。所以我们需要假定的屏幕矩形,以及假定的单位像素,用于运行在真实屏幕中时,映射到屏幕上,当然这个映射关系应该是可配置的,参数是可以调整的,那么让我们来看一下Unity为我们提供的CanvasScaler组件,他实现了什么功能吧
Tips:首先在这里普及一个概念,Pixels Per Unit 这是导入图片时需要设置的图片像素单位大小,指代的意思是一个Unity3D单位对应多少个图片像素 ,默认值为100,一般情况下这个值只需要统一就可以了,使用默认值既可
基于像素大小的缩放模式
此模式下屏幕分辨率完全与图片像素对齐,也就是说在屏幕分辨率小的情况下,图片显示在屏幕中会变大,屏幕分辨率大的情况下,图片显示会变小,
ScaleFactor: 缩放因子,用来缩放画布下的元素
基于屏幕宽高比匹配的自适应
ReferenceResolution: 参考分辨率,这个很重要,这个一般作为美术出图的规范,如果在这里基调下进行开发,美术给定个参考图应与此参考分辨率一致
ScreenMatchMode
- MatchWidthOrHeight: 以宽度为参考、以高度为参考或介于两者之间的其他值缩放画布区域,这么说可能有点抽象,这是一个宽高比的影响因子,越小宽度变化越小,越大高度变化越小,假使值为0,则屏幕宽度不变,进行高度自适应,反之亦然,
- Expand: 水平或垂直扩展画布区域,因此画布的大小永远不会小于参考。
- Shrink: 水平或垂直裁剪画布区域,因此画布的大小永远不会大于参考。
基于物理大小的缩放
就像你们理解的那样基于真实的屏幕的大小,参考值是一个屏幕DPI。DPI是单位面积内的像素点数。DPI是一个量度单位,有很多可选单位。
总结
通常我们使用适应基于屏幕宽高比匹配的自适应,因为在这种情况下,不管玩家在什么分辨率下或者是宽高比下,屏幕内的元素显示,相对于屏幕大小,总是趋近于相同的。
RectTransform锚点自适应
以上这个自适应的例子很好的诠释了自适应的应用,
上方四个属性(例:Left)描述了当前图片和锚点的位置关系,
Anchors 这个属性是一个屏幕0-1的映射,调整这个值可以控制物体锚点的位置,并且是基于屏幕百分比的,
LayoutGroup 元素排列自适应
LayoutElement
每个UI元素,也就是在Canvas
下的元素,都会自动被挂上RectTransform
以及有一层默认的布局属性。
那么让我们先来解释一下这几个属性的作用。
- Min Width 最小宽度,只要设置了值,不管布局组件怎么设置,他永远最小也有这么大
- Min Height 最小高度,结论同上
- Preferred Width 优选宽度,会优先选择这个值作为布局计算参考
- Preferred Height 优选高度,结论同上
- Flexible Width 如果有额外的可用空间,应该分配这个布局元素的额外相对高度。
- Flexible Height 结论同上
如果在这个gameObject上挂载LayoutElement
组件,这些属性就会被LayoutElement上的设置覆盖。
这个组件上面多了一个属性LayoutPriority
,这个属性作用是在计算布局的时候进行优先级排序,数字越大的组件布局的优先级的越高
LayoutGroup
让我们先从介绍面板开始
- Padding 内边距
- Spaceing 子布局元素的间隔
- Child Alignment 子布局元素的对齐方式
- Control Child Size 是否强制控制子物体的高度或宽度
- Use Child Scale 如果勾选了这个选项,会在计算布局宽度或者高度的时候把这个Scale也算进去,用这个物体真实的大小,
- Child Force Expand 如果勾选了这个选项,在默认情况下,子布局元素会填充
RectTransform
规定的宽高,并且平均分配大小
上面我讲解很抽象,直接看图例吧
无
Child Force Expand 强制扩张并平分间隙
Control Child Size 这里使用了子布局元素的优选宽度和高度,尽管没有被重载,依然有10
Use Child Scale 这里使用了开启与不开启进行比较
接下来是本文的重点,当父LayoutGroup控制子LayoutElement来自动计算大小以及布局的时候(也就是勾选了Control Child Size
以及 Child Force Expand
),我们重载的布局信息在这个时候就会发生明显的作用,我们来整理一下这个计算方式,父物体的宽度为400,然后把子布局元素的Min Width设置为150,
好像和我们想象的有点不太一样了,为什么他是205.。。。。。
实际上这个Min Width不是我们想象的他会固定这个最小值,他值的是计算布局的时候会使用这个最小值作为参考,并且计算完成的值不会小于这个值,其他子元素没有LayoutElement挂载,由上面的图可知,其他元素的Preferred Width值为10,group勾选强制扩张子元素,那么让我们拿到他用来计算参考值
child | prop | value |
1 | Min Width | 150 |
2 | Preferred Width | 10 |
3 | Preferred Width | 10 |
4 | Preferred Width | 10 |
150+10+10+10=180 总参考值占位
400-180=220 剩下的部分
220/4=55 剩下的部分平分到各个子物体
child | result |
1 | 205 |
2 | 65 |
3 | 65 |
4 | 65 |
让我们看一下我们得出的结论是不是对的,我们可以通过下图发现我们的结果是对的
这里主要要说明的点就是LayoutElement
上面的属性值都是计算的时候的参考值而非最后结果
那么再让我们看一下 Flexible Width
的作用,我把 Flexible Width
设置成 3
可以看到这里最后的结果是260,那让我们思考一下,目前需要分配的额外空间是够的,因为所有的参考值加起来目前是 180,那么我们猜测他是一个用来分配剩余空间的权重值,那我们继续计算
220/(3+1+1+1)*3=110
150+110=260
可以看出我们的结论是对的,我们把所有剩余空间用利用权重平分以后,得到了我们当前这个元素的宽度
那么再让我们总结一下,
- Min Width 最小宽度,只要设置了值,不管布局组件怎么设置,他永远最小也有这么大
- Min Height 最小高度,结论同上
- Preferred Width 优选宽度,会优先选择这个值作为布局计算参考
- Preferred Height 优选高度,结论同上
- Flexible Width 如果有额外的空间,会利用这个作用权重来求值获取最后的结果
- Flexible Height 结论同上
**顺带再提一下 contentSizeFitter
这个组件他会根据子元素的 Min
属性 或者 Preferred
属性 来装配生成当前物体的大小,如图
总结
相信通过这篇文章,大家会对UGUI的布局和组件排列有一个深刻的认识。具体实现逻辑大家可以通过阅读源码来对自己不知道的细节进行补充,或者关注我,给我发私信,我也会乐意解答
结语
那么今天就教程就到这里结束了,如果觉得我说的有用的话就在我的>>>>>> 戳这里 github项目<<<<<<点上一个小小的star吧,屑屑(比心)