从锚点来谈 Rect Transform 组件
前言
手游开发过程中,由于各种不同分辨率手机的存在,为此 UI 相对位置布局是必不可少的一步。在使用 Unity UI 开发界面时,要实现一个 UI 元素的位置相对父元素不发生变化,脑海中能想到的可能有以下几种实现方式:
编写脚本,根据父元素的位置、大小等信息来动态设定当前元素的位置,使其能够保证位置相对父元素的位置不变化
使用 Rect Transform 组件的 Anchors 属性设置,保证相对位置布局
...
接下来我们就来看看这个 Anchors。
Anchors
Anchors 由两个归一化的 Vector 组成 - Min 和 Max,这两个向量的值都是在父节点中并且归一化的值。两个向量会组成一个矩形,矩形的四个顶点就是四个 Anchor(锚点),这两个向量对应了 RectTransform 类中的 anchorMin
和 anchorMax
属性。Unity 中,Anchors 是可以手动编辑的,如下:
上图中 Rect Transform 下的 Anchors Min 和 Max 就是锚点构成的矩形左下角和右上角的坐标,当这些坐标设置为不同的值时,其所在的 UI 元素的位置和尺寸大小将由不同的属性来控制。
- 默认情况下,Min 和 Max 的值都为 (0.5, 0.5),这时构成的锚点矩形就是一个点(四个锚点发生重合),位于父节点 UI 元素的正中间。此时当前 UI 元素的位置由 Pos X 和 Pos Y 表示,这两个属性是相对 Anchor 的位置,即 RectTransform 类中的
anchoredPosition
属性。如果 Anchor 四个锚点分散开,那么就是相对 Anchors 构成矩形的中心点的位置。尺寸大小由 Width 和 Height 表示,如下图:
上图测试 UI 元素的 Pivot (支点/中心点)默认为 (0.5, 0.5)。定位、缩放和旋转都会根据 Pivot 的值来执行。
改变父节点 UI 元素的大小尺寸不会改变当前 UI 元素的大小尺寸且 UI 元素的位置永远位于父节点 UI 元素的正中间,如下:
四个锚点重合然后设置 Pos X 和 Pos Y,是常用的一种相对布局的方式。例如在 Canvas 中,有一个按钮需要始终位于 Canvas 的右上角某个位置,由于Canvas 会在不同的分辨率下自适应大小,因此此时只需要设置四个锚点都位于 Canvas 的右上角,即 Min 为 (1, 1),Max 也设置为 (1, 1),然后设置按钮的 Pos X 和 Pos Y 就是按钮相对 Canvas 右上角的距离长度。如下所示:
改变 Canvas 的分辨率,观察 UI 元素相对 Canvas 的位置不会发生变化。如下图:
Unity 中默认提供了多种实现相对布局设置锚点的快捷方式:
- 接着讲 Min 设置为 (0, 0),Max 设置为 (1, 1),其实可以看出 UI 元素的位置以及尺寸大小由 Left、Top、Pos Z、Right 和 Bottom 共同控制。如下图:
改变 Left、Top、Right 和 Bottom 中的这些值,UI 元素的位置或尺寸大小都有可能发生变化,如下所示:
可以看出,此时的四个锚点分别位于父节点元素的四个顶点。
Left 决定了 UI 元素所在矩形的左边(不是 Pivot 中心点)距离锚点构成矩形左边的长度
Top 决定了 UI 元素所在矩形的上边距离锚点构成矩形上边的长度
Right 决定了 UI 元素所在矩形的右边距离锚点构成矩形右边的长度
Bottom 决定了 UI 元素所在矩形的下边距离锚点构成矩形下边的长度
改变上面这些值,会改变当前 UI 元素的尺寸大小,改变父节点 UI 元素的大小也会改变当前 UI 元素的尺寸大小(因为锚点矩形决定的相对距离值),如下:
- 下面继续进行试验,将 Min 设置为 (0, 1),Max 设置为 (1, 1),此时锚点构成的矩形是一条直线,位于父节点元素所在矩形的上边。如下所示:
此时,位置和大小由以下属性决定:
Left 决定了当前 UI 元素所在矩形的左边距离锚点构成矩形(这里是一条直线,即最左边的那个点的 X 坐标)的长度
Pos Y 决定了当前 UI 元素的 Y 坐标,它是相对于锚点的 Y 坐标的(即 Y 坐标所在的值是坐标系的零值)
Right 决定了当前 UI 元素所在矩形的右边距离锚点构成矩形(这里是一条直线,即最右边的那个点的 X 坐标)的长度
Height 就是 UI 元素的高度,父节点 UI 元素高度改变也不会改变当前 UI 元素的高度。因为高度有这个值决定
根据类似的设置,也可以让锚点组成的矩形(直线)分别位于左边、右边或下边,这样能做到某一个方向尺寸大小固定,位置相对布局,另一个方向尺寸大小跟随父元素变化,相对父元素距离不发生变化。
Rect Transform 组件
分析完了最常用了 Anchors,下面来到锚点的载体 Rect Transform 组件,它在 Unity 编辑器呈现如下图:
那么什么是 Rect Transform?它的作用又是什么?首先来看看官方文档的定义:
The Rect Transform component is the 2D layout counterpart of the Transform component. Where Transform represents a single point, Rect Transform represent a rectangle that a UI element can be placed inside. If the parent of a Rect Transform is also a Rect Transform, the child Rect Transform can also specify how it should be positioned and sized relative to the parent rectangle.
RectTransform 组件是 2D UI 中常用的一个组件,它代表了 UI 元素构成布局的一个矩形区域;在 RectTransform 组件中存储了 position、size 和 anchoring 等信息(比如上面讲过的 Anchors 和后面将要讲到的其它属性),这些信息将指定一个 UI 元素的的位置、尺寸大小和如何缩放等;另外 RectTransform 继承自 Transform 类,因此它也拥有 Transform 类中诸如 position
、localPosition
等属性。
接着再来看看 Rect Transform 组件的其它属性。
RectTransform 中的其它属性
首先,在 Unity 编辑面板上可以编辑的属性有以下这些:
Pos (X, Y, Z) - 在前边分析 Anchors 的时候也提到过 Pos X 和 Pos Y,当四个锚点重合这两个属性就是当前 UI 元素的支点(中心点)相对重合锚点的位置(x 和 y 坐标);如果 Anchor 四个锚点分散开,那么就是 UI 元素的支点(中心点)相对 Anchors 构成矩形的中心点的位置;Pos X 和 Pos Y 对应了 RectTransform 类中的
anchoredPosition
成员变量。Pos Z 在 Canvas 的 Render Mode 设置为不同模式时,有不同的意义: 当 Render Mode 为Screen Space - Overlay
或Screen Space - Camera
(不设置 Camera)时,表示当前 UI 元素相对于父元素的 Z 偏移量(一个 UI 单位和世界空间下单位之间的转换与当前 RectTransform 祖先元素的 Scale 相关);当为Screen Space - Camera
并设置 Camera 时,Pos Z 等于 UI 元素的 localPosition.z,真正的 World Position 的 z 坐标和其祖先元素的 Scale、Canvas 的Plane Distance
以及相机的 FOV等信息相关;当设置为为World Space
时,表示当前 UI 元素相对于父元素的 Z 偏移量(UI 单位和世界空间下单位之间的转换与当前 RectTransform 祖先元素的 Scale 相关,因此可以根据各个祖先的这个值计算出当前 UI 元素世界空间下 Z 方向的坐标)。Width/Height - 当前 UI 元素所在矩形的宽和高,对应了 RectTransform 类中的
rect
中的 m_Width 和 m_Height。Left、Top、Right 和 Bottom - 当前 UI 元素所在矩形的四条边相对于锚点构成的矩形四条边的距离长度。当 Anchors 设置为拉伸(四个锚点不全部重合)的时候,在 Unity 编辑器界面可以设置不同的属性。
Anchors - 锚点属性上面我们详细分析过,这里再次提一下。它包含 Min 和 Max 两个向量,分别代表锚点在父节点 UI 元素中构成的矩形的左下角和右上角的归一化坐标。
Min - 锚点构成的矩形左下角(相对父节点尺寸)的归一化坐标,(0,0) 代表父节点左下角,(1,1) 代表父节点右上角;对应了 RectTransform 类中的
anchorMin
成员变量。Max - 锚点构成的矩形右上角(相对父节点尺寸)的归一化坐标,(0,0) 代表父节点左下角,(1,1) 代表父节点右上角;对应了 RectTransform 类中的
anchorMax
成员变量。
Pivot - 当前 UI 元素的矩形支点(中心点),这个值是相对自身而言的,并且归一化;元素旋转、缩放都围绕着这个支点进行;对应了 RectTransform 类中的
pivot
变量。Rotation - 围绕 X、Y 和 Z 三个轴的旋转角度(单位: 度)。
Scale - X、Y 和 Z 三个维度上的缩放系数。
上面的这些属性计算是在一帧的末尾进行的(计算顶点数据之前),这样能够保证计算这些数据的时候所有的参数都是最新的。因此在第一次回调 Start
和 Update
方法的时候这些数据还未被计算好;在这种情况下若需要使用这些属性,可以调用 Canvas.ForceUpdateCanvases()
方法强制更新这些数据。
在 RectTransform 类中还有一些其它属性,如下:
position - 在世界空间下的位置
localPosition - 在父节点空间下的位置,父节点坐标系以其支点(中心点) Pivot 为原点(单位是像素)
anchoredPosition - 支点(中心点)相对锚点构成矩形中心点的位置
anchoredPosition3D - 这个属性和 anchoredPosition 有点类似,代表了当前 UI 元素矩形支点相对 Anchors 的 3D 位置,其值返回是 (anchoredPosition.x, anchoredPosition.y, localPosition.z)
offsetMax - 元素矩形右上角相对锚点构成矩形右上角的位移
offsetMin - 元素矩形左下角相对锚点构成矩形左下角的位移
rect - The calculated rectangle in the local space of the Transform.
x - 当前 UI 元素所在矩形的左下角 X 坐标(相对自身支点(中心点) Pivot)
y - 当前 UI 元素所在矩形的左下角 Y 坐标(相对自身支点(中心点) Pivot)
width - 当前 UI 元素所在矩形的宽度
height - 当前 UI 元素所在矩形的高度
center - 当前 UI 元素所在矩形的中心点位置(相对自身支点(中心点) Pivot)
xMin - 当前 UI 元素所在矩形的最小 X 坐标(相对自身支点(中心点) Pivot)
... (更多属性请参考文档)
sizeDelta - 当锚点不重合时,这个属性就是当前 UI 元素所在的矩形的宽高与 Anchors 构成的矩形的宽高的差值;当四个锚点重合时,这个值就是 RectTransform 的宽和高
RectTransform 源码分析
讲过了大部分 RectTransform 类的成员属性变量,最后我们再来看看 RectTransform 部分源码。
GetLocalCorners 方法
这个方法很简单,就是根据当前 RectTransform 的 rect
内部的 x、y、xMax 和 yMax 等数据返回本地坐标系中当前 UI 元素所在矩形四个角的位置(从左小角开始依次顺时针返回)。代码如下:
|
|
GetWorldCorners 方法
与 GetLocalCorners
相对应,返回世界空间下当前 UI 元素所在矩形四个角的位置,代码如下:
|
|
代码也很简单,获取本地空间下的四个角的坐标,然后调用 Transform.TransformPoint 方法将本地空间下的这四个角的位置转换到世界空间下并返回。
GetRectInParentSpace 方法
获取父节点空间下当前 RectTransform 的 rect
的表示。
SetInsetAndSizeFromParentEdge 方法
根据指定边设置当前 UI 元素的相对父元素矩形的距离,同时设置 UI 元素尺寸大小;调用该方法后,当前 RectTransform 的 anchorMin、anchorMax、sizeDelta 和 anchoredPosition 等属性都会被修改。
SetSizeWithCurrentAnchors 方法
设定指定轴上 RectTransform 的尺寸大小。代码如下:
|
|
计算过程也很简单。之前说过在锚点不重合的时候 sizeDelta 就是当前 UI 元素所在的矩形的宽高与 Anchors 构成的矩形的宽高的差值,上面的计算过程就是计算这个差值的过程,根据指定的 size 减去锚点构成矩形的 size 得到 sizeDelta,最后保存到当前 UI 元素的 RectTransform 的 sizeDelta
中。
ForceUpdateRectTransforms 方法
这个方法用来强制重新计算内部的数据。之前提到过 Canvas.ForceUpdateCanvases()
方法也可以完成这样的操作,当 RectTransform 数据发生变化,其会被注册到 CanvasUpdateRegistry 中,当 Canvas.ForceUpdateCanvases()
被调用,CanvasUpdateRegistry 会执行 RectTransform 的重新构建操作从而更新 UI。