UI 布局全解析
Apr 02, 2022确认并解决各种适配模式下,一些RectTransform的大小位置控制问题。
先声明各个属性含义
-
anchorMax 最大的锚点,由(最大的 X 锚点, 最大的 Y 锚点组成),比例值,范围[0, 1]
-
anchorMin 最小的锚点,由(最小的 X 锚点, 最小的 Y 锚点组成),比例只,范围[0, 1]
-
pivot 支点/中心点
-
anchoredPosition 支点相对于锚点的 2D 坐标
-
anchoredPosition3D 支点相对于锚点的 3D 坐标
-
sizeDelta 为 RectTransform 边角到锚点的距离和
-
offsetMin 左下角锚点到 RectTransform 左下角的向量
-
offsetMax 右上角锚点到 RectTransform 右上角的向量
-
rect
-
width 长
-
height 宽
-
注意:当锚点不再一起的时候,anchoredPosition 位置会以将当前的锚点,算出一个中心,anchoredPosition 就是新锚点到支点的向量。
RectTransform 面板几种模式
-
X, Y, Width, Height 为支点锚点的向量,和固定的长宽。
-
Left, Right, Top, Bottom 为 RectTransform 四边相对于 anchor 内缩的长度
-
Left, Right, OffsetY, Height, 左右边自适应,高度偏移,高度固定
-
offsetX, Width, Top, Bottom, 上下自适应,横向偏移,宽度固定
获得准确长宽
由前面可知,sizeDelta 并不是实际 rt 的长宽,但是可以通过公式转换得到实际长宽
realWidth = (anchorMax - anchorMin).x * parent.weidth + sizeDelta.x
realHeight= (anchorMax - anchorMin).y * parent.height + sizeDelta.y
所以可得,当 anchor 为一点时,sizeDelta 为实际长宽
但是这个方法一来要算,而来还要获得父物体的长宽,如果父物体又是自适应的长宽,那就更麻烦了。另外一个方法时直接使用 rt.rect 中的 width 和 height 即可。
注意:rect 的 top, bottom, right, left 已经改为 yMin, yMax, xMax, xMin。并且也不代表 rt 的相对四边 anchor 的内缩量,而是相对 rt 中点的距离。
要获取 top, bottom, right, left 要通过 offsetMin, offsetMax 各个分量获得可得
top = -offsetMax.y
bottom = offsetMin.y
left = offsetMin.x
right = -offsetMax.x
当 anchor 为一点时 (width, height) = offsetMax - offsetMin
设置长宽
SetInsetAndSizeFromParentEdge(RectTransform.Edge, pading, length)
会改变锚点, 位置,以及长/宽。直接根据父物体设置对其方式 Edge 以及边界距离 pading, 长度。举例,当使用 rt.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, 10, 200) 会将 anchorMin 改为 (0, originMinY),anchorMax 改为 (0, originMaxY),然后,rt 左边距离父物体左边界内缩 10,rt宽度 200.
SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, lenght)
直接设置横向或者竖向 rt 的长宽,并且不改变 anchor, pivot 以及位置。即使 pivot 设置为 (0, 0.5),也不会向一方延申设置长宽,以实际的中点延申的长度。
设置属性的顺序是否会有不同结果
屏幕坐标转换
屏幕的左下角为(0,0),右上角为(Screen.width, Screen.height)
如何获取屏幕坐标
-
Canvas 模式为 Overlay 时,RectTransform.position 即屏幕坐标
-
Canvas 模式为 Camera 时
// camer 为 uiRect 所在 Canvas 的相机 screenPos = camera.WorldToScreenPoint(uiRect.position);
//实例化点击事件
PointerEventData eventDataCurrentPosition = new PointerEventData(UnityEngine.EventSystems.EventSystem.current);
//将点击位置的屏幕坐标赋值给点击事件
eventDataCurrentPosition.position = new Vector2(screenPosition.x, screenPosition.y);
List<RaycastResult> results = new List<RaycastResult>();
//向点击处发射射线
EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
return results.Count > 0;
OnStart 时使用会有问题
Comments