RectTransform位置计算方法和UI自适应

UGUI的自适应解决方案

什么是UI自适应?

不同设备的分辨率,宽高比不同。如4K屏幕的像素数是1K屏幕的四倍,在1K屏幕上显示正常的窗口,到了4K屏幕上就会变成原来的四分之一面积,这时候玩家就看不清了。宽高比相同的话我们只要同时缩放即可。

1.以Canvas为根节点,其RectTransform的宽高等于屏幕分辨率。

2.CanvasScaler组件将下面的所有节点都缩放一定倍数。

UIScaleMode为ConstantPixelSize时,固定缩放一定倍数。

UIScaleMode为ScaleWithScreenSize时,根据屏幕分辨率变化。写入基础分辨率,然后自动根据实际分辨率进行缩放。

UIScaleMode为ConstantPhysicalSize时,根据屏幕实际尺寸变化。

而宽高比不同时,需要合理的设置来保持不同UI界面的相对位置。UGUI提供了与父物体角位置不变,与父物体保持边距,与父物体保持比例等方式,下面将详细介绍。

RectTransform的基本字段

在Inspector上右键然后选中Debug,可看到RectTransform上实际存储的字段。

|------------------|---------|
| Anchor | 0~1的比例 |
| AnchoredPosition | 像素值 |
| Pivot | 0~1的比例 |
| SizeDelta | 像素值 |

这四个值是rectTransform的基础属性,其他属性由他们计算得出。

绝对模式的计算流程

当Anchor的最大最小相同时是绝对模式,仅确定一个点。

1.根据Anchor值,按比例在父物体上计算出一个点,可以视为起点。

2.根据AnchoredPosition计算一个偏移,直接加在上一步起点上,计算出一个点,作为中心点。

3.我们最终会计算出UI占据的框,anchors确定了这个框的中心位置。

4.SizeDelta描述了框的宽高。其中心点位置由anchors确定。

编辑器显示上,posX和posY显示的是AnchoredPosition。WIdth和Height显示的是sizeDelta。

相对模式的计算流程

此模式下Anchors的最大最小值不同,确定一个矩形范围。

编辑器显示上,会显示left,right,top,bottom,即上下左右。即实际框距离父物体确定的锚框的像素距离,当父物体锚框改变时,上下左右的距离不变,根据这个计算出节点范围。可实现与父物体上下左右边距的自适应。

当left,right,top,bottom都为0时,字节的大小根据Anchors成比例改变。由此可实现与父物体左半边或者右半边保持一致的自适应。

关于相对模式下left,right,top,bottom与基础值的互相转换

内部代码计算流程如下

1.先根据父物体大小和锚点设置,计算锚框

2.根据pivot确定一个锚点中心点。是的,这步也使用了中心点计算。

3.然后锚点中心点加上AnchoredPosition实现偏移,得到中心点。

4.最后根据实际大小,pivot和中心点,计算节点实际范围。

cs 复制代码
var rtf = transform as RectTransform;
if (!rtf) return;
var parentRTF = rtf.parent as RectTransform;
if (!parentRTF) return;
var parentWidth = parentRTF.rect.width;
var parentHeight = parentRTF.rect.height;
// 先根据父物体大小和锚点设置,计算锚框高度和宽度
var anchorWidth = parentWidth * (rtf.anchorMax.x - rtf.anchorMin.x);
var anchorhHeigth = parentHeight * (rtf.anchorMax.y - rtf.anchorMin.y);
// 计算节点实际高度和宽度。
// 绝对模式下anchorWidth为0,sizeDelta为正,大小与实际宽高相同。
// 相对模式下anchorWidth不为0,sizeDelta大小为-(left+right)
var realWidth = anchorWidth + rtf.sizeDelta.x;
var realHeight = anchorhHeigth + rtf.sizeDelta.y;
// 根据pivot,在锚框中确定一个锚点,然后用anchoredPosition偏移得到中心点,再用实际大小和pivot计算得到实际节点大小。
// 由此我们可以确定上下左右的表达式。
var left = (anchorWidth - realWidth) * rtf.pivot.x + rtf.anchoredPosition.x;
var right = (anchorWidth - realWidth) * (1 - rtf.pivot.x) - rtf.anchoredPosition.x;
var bottom = (anchorhHeigth - realHeight) * rtf.pivot.y + rtf.anchoredPosition.y;
var top = (anchorhHeigth - realHeight) * (1 - rtf.pivot.y) - rtf.anchoredPosition.y;
// 将realWidth带入化简得到
left = -rtf.sizeDelta.x * rtf.pivot.x + rtf.anchoredPosition.x; //1
right = -rtf.sizeDelta.x * (1 - rtf.pivot.x) - rtf.anchoredPosition.x; //2
bottom = -rtf.sizeDelta.y * rtf.pivot.y + rtf.anchoredPosition.y; //3
top = -rtf.sizeDelta.y * (1 - rtf.pivot.y) - rtf.anchoredPosition.y; //4
////////////////////////////////////////////
// 根据上下左右的表达式,稍加化简即可得到sizeDelta和anchoredPosition的表达式
var sizeDelta = new Vector2(
    -left - right,
    -top - bottom);
var anchoredPosition = new Vector2(
    left - (left + right) * rtf.pivot.x,
    bottom - (bottom + top) * rtf.pivot.y);
// 由此我们得到了相对模式下left,right, top, bottom的互相转换方法

showText.text = $"left = {left}, top = {top}\n" +
                $"right = {right}, bottom = {bottom}\n" +
                $"sizeDelta = {sizeDelta}\n" +
                $"anchoredPosition = {anchoredPosition}\n" +
                $"rect={rtf.rect.size}";
// 建议使用RectTransform.rect.size来获取大小,这个办法不因锚点设置改变。

此设计的优点

可以看到,UGUI这个位置确定十分繁琐,那么他到底实现了那些功能?

1.AnchoredPosition和其他软件中位置功能类似,可以视为位置偏移量。

2.Anchor在这个位置的基础上,实现了位置起点和终点能自由设置,而其他软件中起点终点往往固定。这样在我们做从左到右平铺的列表时,将Pivot设为最左就能很轻松的计算每个元素坐标。

3.Pivot确定的中心点,也是是缩放和旋转的中心,可以控制元素如何缩放和旋转。比如控制放大时,仅向下增加高度,或者仅向上增加宽度。

4.优雅的实现屏幕自适应。将Anchor设置到右上角,就能实现固定在屏幕右上角的小地图等。

5.同时能实现与父物体保持边距的自适应,和与父物体保持比例的自适应。

此设计的缺点

1.总体设计相当复杂,难以理解和上手。

2.难以比较两个非同父物体节点的位置。

3.想要将一个节点设置到与另一个节点大小位置相同,会很困难。

快速设置锚点和中心点的小技巧

点击红框区域会出现锚点预设窗口。点击可快速设置锚点位置,实际使用中基本都是这里面的几个预设。按住shift可同步设置中心点,按住alt可同步修改位置。

关于UGUI的三个坐标系和坐标转换

屏幕坐标指真实分辨率下像素的坐标,根据设备不同,分辨率不同,有很大区别。

UGUI坐标指在UGUI内使用的坐标,我们在Inspector上看到的数值都是UGUI坐标,他与设备分辨率无关,根据自适应流程自动转换为屏幕坐标。

世界坐标指ui的3D坐标。所有UI最终都会转换为3D空间中的面片渲染,因此每个UI都会有世界坐标。

一些接口返回的坐标会是其他坐标系下的,这时就需要我们进行坐标转换。例如:

Input.mousePostion获取屏幕坐标(绝对坐标)

recttransform.postion获取UI的世界坐标(绝对坐标)

recttransform.ancherpostion获取锚点坐标,即UGUI坐标(相对坐标)

RectTransformUtility.ScreenPointToWorldPointInRectangle屏幕坐标转世界坐标

RectTransformUtility.ScreenPointToLocalPointInRectangle屏幕坐标转UGUI坐标

在三个坐标系下转换可见下图

如果要将点在不同UGUI坐标下转换,可以先转成世界坐标,再转成本地坐标。

相关推荐
世洋Blog12 小时前
Unity发布自己的插件包
unity·游戏引擎
ytttr87321 小时前
基于C#的CAN总线数据解析BMS上位机
android·unity·c#
雪下的新火1 天前
ASE07-魔法药剂炼制效果
经验分享·unity·shader·ase·游戏效果
璞瑜无文1 天前
Unity 游戏开发之入门
unity·游戏引擎
YANshangqian1 天前
免Adobe Photoshop 2025
ui·adobe·photoshop
毛甘木1 天前
Unity ComputeShader 基础语法与使用教程
unity·computeshader
天天进步20151 天前
【Cradle 源码解析二】由眼入心:LMM 如何“看懂”屏幕与 UI 识别机制
ui
小清兔1 天前
一个unity中URP的环境下旋转天空盒的脚本(RotationSky)
开发语言·数据库·学习·程序人生·unity·c#·游戏引擎
EQ-雪梨蛋花汤1 天前
【Unity笔记】 WorldStreamer2指南——针对大世界的流式加载与优化
笔记·unity·游戏引擎