RectTransform详解

示意图

image

 

1) anchor区域(浅绿色方框区域)

Vector2 anchorMin;
anchor区域左下角与parent左下角的距离百分比(用parent大小的百分比表示)

Vector2 anchorMax;
anchor区域的右上角与parent右上角的距离百分比(用parent大小的百分比表示)

 

上图中parent的大小为300*200,所以anchorMin的距离为(60, 20) ,anchorMax的距离为(90, 20)

 

2) 节点坐标: 是相对anchor区域

Vector2 offsetMin;
节点左下角相对anchor区域左下角的坐标

Vector2 offsetMax;
节点右上角相对anchor区域右上角的坐标

 

上面的图中:
offsetMin=(40, 30)
offsetMax=(-10, -30)

 

3) Vector2 sizeDelta;

节点自身大小与anchor区域大小的差值(即上图的白色区域减绿色方框区域),计算公式: sizeDelta=rectTransform.rect.size - anchorAreaSize=offsetMax-offsetMin

上面的图中:
sizeDelta=(-10, -30) - (40, 30)=(-50, -60)

 

4) Vector2 anchoredPosition;

表示自己的pivot相对parent的虚拟pivot的坐标

image

虚拟pivot的计算:

Vector2 virtualPivot = new Vector2(
  Mathf.Lerp(anchorMin.x, anchorMax.x, pivot.x),
  Mathf.Lerp(anchorMin.y, anchorMax.y, pivot.y)
);

a) 计算公式1:anchoredPosition = localPosition - parentRtf.rect.size × ( virtualPivot - (0.5, 0.5) )

上图中的virtualPivot = new Vector2(

  Mathf.Lerp(0.2, 0.7, 0.4),

  Mathf.Lerp(0.1, 0.9, 0.5)

) = (0.2 + 0.5*0.4, 0.1 + 0.8*0.5) = (0.4, 0.5), 即anchorArea的(40%宽度, 50%高度)

anchoredPosition = (-10, 0) - (300, 200) × ( (0.4, 0.5) - (0.5, 0.5) ) = (-10, 0) - (300, 200) × (-0.1, 0) = (20, 0)

b) 计算公式2: anchoredPosition = offsetMin + sizeDelta * pivot

上面的图中: anchoredPosition = (40, 30) + (-50, -60) * (0.4, 0.5) = (20, 0)

c) 根据sizeDelta=offsetMax-offsetMin, 还可以推导出: anchoredPosition = offsetMin + (offsetMax-offsetMin)*pivot = offsetMin*(1-pivot) + offsetMax*pivot

d) 一般anchors在横向和竖向都没有设置stretch拉伸时,会用anchoredPosition来设置节点坐标:

因为此时, anchorAreaSize为(0, 0), sizeDelta就是节点自身的大小, anchoredPosition就是pivot的坐标(以anchor区域的pivot为原点)

 

5) Rect rect;

rect.position或rect.min: 节点左下角相对pivot的坐标
rect.center: 中心点相对pivot的坐标。center=(0, 0)时, 表示pivot和中心点重合
rect.max: 右上角相对pivot的坐标

image

rect.size: 节点的宽高

 

6) Vector3 localPosition;

自己的pivot相对parent的pivot的坐标。

根据localPosition推导出的一些计算公式:

  a) 自己的中心点相对于parent的pivot坐标:rtf.localPosition + rtf.rect.center,即图中土黄色向量

image

  b) 自己的中心点相对于parent的左下角: rtf.localPosition + rtf.rect.center + parentRtf.rect.size * parentRtf.pivot,即图中粉红色向量

image

   c) 自己的左下角相对于parent的pivot坐标:rtf.localPosition + rtf.rect.min,即图中土黄色向量

image

   d) 自己的左下角相对于parent的左下角坐标:rtf.localPosition + rtf.rect.min + parentRtf.rect.size * parentRtf.pivot,即图中粉红色向量

image

  e) 自己的左上角相对parent左下角的坐标:rtf.localPosition + rtf.rect.min + parentRtf.rect.size * parentRtf.pivot + new Vector2(0, rtf.rect.size.y),即图中土黄色向量

image

 

将节点按中心点对齐

//将a的中心对齐b的中心
public static void SetCenterSameAs(RectTransform rtfA, RectTransform rtfB, Camera uiCam)
{
    Vector3 bLPos = rtfB.localPosition;
    Vector3 bRectCenter = rtfB.rect.center;
    Vector3 bCenterLPos = bLPos + bRectCenter;
    Vector3 bCenterWPos = rtfB.parent.TransformPoint(bCenterLPos);

    Vector2 bCenterScreenPos = RectTransformUtility.WorldToScreenPoint(uiCam, bCenterWPos); //b中心对应的屏幕坐标
    RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)rtfA.parent, bCenterScreenPos, uiCam, out Vector2 aCenterLPos); //相对a父物体pivot的localPosition

    Vector2 aLPos = aCenterLPos - rtfA.rect.center;
    rtfA.localPosition = aLPos;
}

 

 

节点与parent的边距计算

1) 不考虑节点自身缩放时

left边距 = anchorMin.x * parent宽度 + offsetMin.x = 0.2 * 300 + 40 = 100
bottom边距 = anchorMin.y * parent高度 + offsetMin.y = 0.1 * 200 + 30 = 50
right边距 = (1 - anchorMax.x) * parent宽度 - offsetMax.x = (1 - 0.7) * 300 + 10 = 100
top边距 = (1 - anchorMax.y) * parent高度 - offsetMax.y = (1 - 0.9) * 200 + 30 = 50

 

2) 考虑节点自身缩放

left边距 = anchorMin.x * parent宽度 + offsetMin.x + pivot.x * ((1 - scale.x) * 节点缩放前宽度) = 0.2*300 + 40 + 0.4*((1-0.8)*100) = 108
bottom边距 = anchorMin.y * parent高度 + offsetMin.y + pivot.y * ((1 - scale.y) * 节点缩放前高度) = 0.1*200 + 30 + 0.5*((1-1)*100) = 50
right边距 = (1 - anchorMax.x) * parent宽度 - offsetMax.x + (1 - pivot.x) * ((1 - scale.x) * 节点缩放前宽度) = (1-0.7)*300 + 10 + (1-0.4)*((1-0.8)*100) = 112
top边距 = (1 - anchorMax.y) * parent高度 - offsetMax.y + (1 - pivot.y) * ((1 - scale.y) * 节点缩放前高度) = (1-0.9)*200 + 30 + (1-0.5)((1-1)*100) = 50

 

根据边距设置节点坐标

1) 根据上面的推导,我们自己实现的

public static void SetLocalPosByLeftTopMargin(RectTransform rtf, float leftMargin, float topMargin, Rect parentRect)
{
    var rect = rtf.rect;
    var pivot = rtf.pivot;
    var localScale = rtf.localScale;

    var offsetMin = rtf.offsetMin;
    var offsetMax = rtf.offsetMax;

    var anchorMin = rtf.anchorMin;
    var anchorMax = rtf.anchorMax;

    offsetMin.x = leftMargin - anchorMin.x * parentRect.width - rect.width * pivot.x * (1 - localScale.x);
    offsetMax.x = leftMargin + rect.width - anchorMax.x * parentRect.width;

    offsetMin.y = parentRect.height - topMargin - rect.height - anchorMin.y * parentRect.height;
    offsetMax.y = (1 - anchorMax.y) * parentRect.height + rect.height * (1 - pivot.y) * (1 - localScale.y) - topMargin;

    rtf.offsetMin = offsetMin;
    rtf.offsetMax = offsetMax;
}

 

2) RectTransform上其实也有类似的函数

SetInsetAndSizeFromParentEdge

void Start()
{
    var rtf = GetComponent<RectTransform>();
    var rect = rtf.rect;
    rtf.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, 100, rect.width);
    rtf.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 50, rect.height);
}

注意:这个函数执行后,会修改anchorMin和anchorMax的值

 

 

参考

Unity3D RectTransform使用详解:布局、属性、方法 - 知乎 (zhihu.com)

 

posted @ 2022-11-26 00:36  yanghui01  阅读(943)  评论(1)    收藏  举报