Contents

01RectTransform相关

RectTransform

本文主要记录一下跟RectTransform相关的操作说明。

其实对于UI布局系统来说,其数学部分已经非常清楚了,详细可以看01UI布局锚点结构。但是每个引擎对相关概念的接口封装不尽相同。有的除了一些基础概念之外还增添了一些独有概念的字段,对于每个参数的操作也都有不同的方式。所以在此记录一下Unity中对RectTransform中操作的细节。

字段说明

根据官方文档https://docs.unity3d.com/ScriptReference/RectTransform.html。 Unity中RectTransform主要有一下字段: 其中大部分字段都跟UI布局中的数学概念对应,例如Pivot,Anchor等。

字段名称 官方字段含义
anchoredPosition 该RectTransform的Pivot相对于其Anchor的位置
anchoredPosition3D 该RectTransform的Pivot相对于其Anchor的3D坐标位置
anchorMax Anchor中右上角的归一化坐标(是一个相相对于父对象的比例值)
anchorMin Anchor中左下角的归一化坐标(是一个相相对于父对象的比例值)
drivenByObject 表示是否被某个对象驱动控制布局参数
offsetMax 该RectTramsform的右上角点相对于Anchor右上角点的偏移
offsetMin 该RectTramsform的左下角点相对于Anchor左下角点的偏移
pivot 该RectTransofrm的Pivor点定位置
rect 该RectTransform所表示的矩形的描述
sizeDelta 该RectTramsform相对于Anchor之间的大小

anchoredPosition

含义说明

即该RectTransform中Pivot点相对于Anchor的位置,如下图:

但根据布局锚点的数学结构来说,可以简单看到。对于锚点退化成点的情况下来说,该值就相当于PosX,PosY。如果是线的情况下实际上是通过Offset来计算的,但是该值实际上任然存在。

实际上该值为:

$$Pos=OffsetMin+(OffsetMax-OffsetMin)*Pivot=OffsetMin+SizeDelta*Pivot$$

参考OffsetMin,OffsetMax的含义。当Anchor退化成点的情况,OffsetMax,OffsetMin相当于从一个点出发到当前RectTransform右上角,左下角点的两个向量。所以该公式值就是Anchor点到Pivot所对应点的向量,这也相当于子RectTransform相对于Anchor的位置坐标。当Anchor不是点的时候OffsetMax,OffsetMin尾部不再重叠,此时含义就不再是Anchor到Pivot对应点的向量。此时谈论子RectTransform相对Anchor的位置也不具有实际意义,这个时候Anchor也不是一个简单的点。而是一个区域。

操作效果

对于AnchoredPosition赋值,不论什么情况下都将设置AnchorPosition值为目标值,保持SizeDelta和Pivot不变,修改OffsetMax和OffsetMin来使得上述公式计算值为目标值。所以:

  • 当Anchor为点的时候: 相当于直接操作Pivot相对于Anchor点的距离,即移动子RectTransform。
  • 当Anchor不为点的时候: 由于保持SizeDelta不变,即OffsetMax-OffsetMin不变。所以相当于调整OffsetMin值,使得其值增加对应数值。

offset

含义说明

即该RectTransform中右上角,左下角点相对于Anchor右上角,左下角点的偏移。示例如下图:

当设置Anchor不为点的情况,此时可以看到设置面板也不再是点情况下的PosX,PoxY,Width,Height的组合。而是Left,Top,Right,Bottom的组合。可以看到:

$$ OffsetMax = (-Right,-Top),OffsetMin = (Left,Bottom) $$

但是不论什么情况下,无论Anchor是否为点。OffsetMax,OffsetMin的语义都是确定的,即图中所表示的两条向量。

操作效果

对于Offset赋值,不论什么情况下都将设置Offset值为目标值。于是子RectTransform所形成的矩形区域会对应修改,然后根据矩形区域和Pivot点,重新按照公式修改AnchoredPosition值与SizeDelta值。

实际上这个修改过程也与UI布局中的数学过程所对应。即先按照比例计算一次Anchor值,然后在Anchor的基础上再做一次左下顶点,右上顶点的平移操作。这里修改的就是直接修改第二次从Anchor计算子RectTransform矩阵中参数值。

sizeDelta

含义说明

sizeDelta值实际即

$$SizeDelta=(OffsetMax-OffsetMin)$$

如下图:

如果Anchor退化成点,可以看到,这实际上就是当前RectTramsform的对角线向量,表示了这个Rect的大小。但是如果Anchor没有退化成点,可以看到OffsetMax,OffsetMin尾部不重叠。此时SizeDelta的语义不再是RectTransform的对角线向量,实际上混入了Anchor的对角线向量。即

$$OffsetMin+ChildDiagonal = AnchorDiagonal+OffsetMax$$

所以sizeDelta相当于子RectTransform的对角线向量减去Anchor的对角线向量。如果Anchor的对角线向量为0向量。那么就想到子子RectTransform的对角线。

操作效果

对于sizeDelta赋值,不论什么情况下都将设置sizeDelta值为目标值,保持AnchoredPosition大小不变,通过公式反推OffsetMax和OffsetMin的值。所以:

  • 当Anchor为点的时候: 相当于直接操作RectTransform的大小,这个大小会基于Pivot点来进行缩放拉伸。
  • 当Anchor不为点的时候: 根据前面公式,可以看到,这时候也是相当于修改OffsetMin值,使其修改SizeDelta*Pivot的值。另一方面可以根据SizeDelta计算方式发现,其实此时Anchor的对角线向量不可能变化,所以依旧相当于修改子RectTransform对角线变化相同的数值。

Anchor

含义说明

Anchor显然就是Anchor右上角点,左下角点相对于父RectTransform的比例值。如下图:

Anchor的存在相当于建立起了父子RectTransform之间的一个比例变化对象。

操作效果

不论什么情况下都将设置Anchor设为目标值,但保持AnchoredPosition,Offset,SizeDelta都不变。所以最后结果相当于直接修改了中间件的区域大小,进而修改最后绘制出的子RectTransform区域大小。

Rect

该值无法设置,其表示的即当前RectTransform所描绘出的Rectangle区域大小。该值由x,y,Width,Height来构成。分别表示了Pivot在当前Rectangle中的位置以及宽高,如下图:

具体来说

$$ x=-Width*Pivot.x,\space y=-Htight*Pivot.y $$

操作函数

操作函数 官方说明
SetInsetAndSizeFromParentEdge 设置Rectangle相对于父Rectangle指定距离以及大小
SetSizeWithCurrentAnchors 使得RectTransform在指定轴上计算给定大小

SetInsetAndSizeFromParentEdge

该函数有三个参数:

  • 相对边(RectTransform.Edge edge)
  • 间距(flost inset)
  • 大小(float size)

该函数主要是直接设置子RectTransform相对于父RectTransform的边距以及大小。其中边有四种,即上下左右(Top,Bottom,Left,Right)四个边。

其产生的效果主要有三部分:

  • 设置对应的Anchor: 对于Left,Anchor中所有X坐标设置为0,其余不变。Right则所有X坐标设置为1,其余不变。对于Top,Anchor中所有Y坐标设置为1,其余不变。Bottom则所有Y坐标设置为0,其余不变。
  • 使得子RectTransform对应边相对于父RectTransform的对应边相距inset参数大小的值。
  • 使得子RectTransform在对应边轴向上的宽度为目标size值。

所以,对于不在设置轴向上参数,实际上是不会进行修改的。例如设置SetInsetAndSizeFromParentEdge(Edge.Left,10,20)则会修改当前子RectTransform在水平方向上宽度为20,并且距离左侧边有10的距离。

SetSizeWithCurrentAnchors

两个参数:

  • 轴向(RectTransform.Axis axis)
  • 对应方向大小(float size)

该函数主要用来设置RectTransform在目标方向上的大小。前面可以看到类似的字段SizeDelta,但是当Anchor不为点的时候,可以发现通过设置SizeDelta来将RectTransform设置到对应宽度或高度是不准确,虽然其变化量是正确的,但是很多情况下可能需要直接设置到某个数值,而不是先计算之前的数值然后计算变化量。

而该函数则可以设置目标RectTransform的宽度或高度为给定目标值。 该计算保持RectTransform中的Pivot坐标不变。通过修改Offset来实现Rectangle宽度或高度的修改。而Pivot坐标不变,实际上相当于保持AnchoredPosition值不变。

对于RectTransform中Pivot相对于Anchor左下角的坐标$R_{pivot}$以及其对角线向量$R_{diagonal}$其可以表示为

$$ R_{pivot}=OffsetMin+(AnchorDiagonal+OffsetMax-OffsetMin)*Pivot\\ R_{diagonal} = AnchorDiagonal+OffsetMax-OffsetMin=AnchorDiagonal+SizeDelta $$

Anchor不变,所以Anchor的对角线AnchorDiagonal不变。所以$R_{pivot}$不变相当于剩下部分的计算不变,也即AnchorPosition不变。另一方面由上面公式也可以看出,设置对应方向大小,相当于设置$R_{diagonal}$数值。而这里面必定参杂了父对角线AnchorDiagonal的值,这也是为什么通过设置SizeDelta不准的原因。如果要根据数值直接计算,则要涉及父节点的Anchor坐标,这就必须要去父节点上去这个值。这是比较麻烦的一件事情,而该接口正是隐藏了这一点。

RectTransform与Transform

RectTransform继承于Transform。我觉得这有多方面考量原因。

因为Transform描述的是点概念结构,而RectTransform需要描述一个矩形区域概念。所以RectTransform需要扩展一些字段结构。

选择在Transform的基础上扩展结构,来实现自己所描述的概念。这有好处,即RectTransform依旧囊括在Transform之中,可以被基本变换结构操控,当遇到3D结构时,其依旧有效。坏处是,继承于Transform,也就相当于拥有Transform的数据结构字段。这些字段有时会令使用者混淆。

例如RectTransform依旧拥有Transform中的Position,Rotation和Scale数据字段。但是通常UI中并不使用这些字段,而是如上的锚点相关字段。实际上这些字段,依旧有值,并且有其对应含义。

字段名称 字段含义
positon pivot点所在的世界坐标
rotation pivot点所在的世界旋转
scale pivot点所在的世界缩放
localPosition pivot点相对于父对象的坐标
localRotation pivot点相对于父对象的旋转
localScale pivot点相对于父对象的缩放
  • 这里需要注意。即UI所描述的矩形区域也是有着世界坐标的。这个坐标跟我们根节点的Canvas结构挂钩。具体来说,当我们Canvas选定其RenderMode之后,可以看到其有一个不可修改的数值,实际上这个就是根节点Canvas的空间位置。例如如下,我们选择ScreenSpace-Overlay,可以看到在场景中会出现一个跟屏幕分辨率等大的画布。如图,其位置就是$(670.5,285,0)$。其子节点的世界坐标位置position,也就是基于该节点层层变化后的位置。
  • 所以这里可以注意到一件事,有些情况下localPosition会跟anchoredPosition等价。但两者情况语义实际完全不一样。
  • 对于anchoredPosition来说,其将Anchor结构考虑了进去。其本质是相对于Anchor给出点的一个位置偏移。而localPosition则是父子对象Pivot之间的偏移关系。所以对于anchoredPosition来说,其收Anchor影响,有些时候其语义不是很清晰。而localPosition则是相对清晰的,就是Pivot之间偏向量值。

因此如果我们向对坐标点进行坐标变化计算。例如计算Transform A物体下面一个点$A_0$坐标在Transform B下的坐标的时候。可以直接通过Transform A的LocalToWorld矩阵结合Transform B的WorldToLocal矩阵,来算出$A_0$在B下的一个坐标。如果放在RectTransform下面。就是该点相对于RectTransform中Pivot点的坐标距离。需要注意的是,这个相当于localPosition值。而不是能直接设置的anchoredPosition值。