关于RectTransform的一些研究

本文详细解读Unity中的坐标系统,包括世界坐标、屏幕坐标、Rect和RectTransform组件的使用方法,以及如何在不同渲染模式下获取鼠标点击点的像素坐标,并通过实例展示了将图片坐标转换到左下角为起点坐标系的过程。

坐标

unity里面的坐标是笛卡尔坐标系,和flash的还是有区别的..

世界坐标

世界坐标是三维的全局坐标,一般作为基准坐标

屏幕坐标

二维坐标,屏幕左下角是(0,0),右上角是(sizeX,sizeY). flash里面的屏幕左上角是(0,0),右下角是(sizeX,sizeY)

Rect

官网文档Rect

需要注意的是虽然文档写法设置了左上角是起点,但在unity的实际结果中,返回的rect还是以左下角为起点的.

RectTransform里面的三要素
  1. 锚点(四个) 
  2. 角(四个) 
  3. 中心点(一个) 

这三者的关系可以这么形容:在Unity里面,一个UI对象的坐标是指该对象的中心点相对于其父容器的四个锚点upda的中心来定义的,UI对象RectTransform里面的四个角决定了这个对象的宽高.当四个锚点不在一起时,四个锚点和对应的四个点之间的距离决定了该容器相对于父容器的大小.

也就说说锚点和角决定尺寸,锚点和中心点决定位置.

Image[4]

在这张图里,最上面四个小三角就是四个锚点,周围四个实心的点是RectTransform的角,中间的圆环就是中心点,四个锚点在一起,所以对象的尺寸没有随父容器尺寸的变化而变化.

Image(1)[4]

在这张图里面水平锚点被分开了,所以对象的宽度会随着父容器尺寸的变化而变化.

官网关于RectTransform的概括

RectTransform涉及到得API说明
anchoredPosition

中心点相对于四个锚点中点的坐标

rect

rect的x和y返回左下角相对于中心点的距离,w和h返回本身的宽高.

offsetMin和offsetMax

分别指左下角相对于左下角锚点的距离以及右上角相对于右上角锚点的距离

anchorMin和anchorMax

这个是针对锚点的,锚点时相对于父容器定义的,所以这两个属性也是相对于父容器的.分别指锚点占父容器尺寸的百分比位置.

sizeDelta

这个值挺好玩的,如果四个锚点都在一定,就是宽度和高度,如果水平的锚点分开了,y还是高度,x变成了-(left+right).如果垂直的锚点分开了,x还是宽度,y变成了-(top+bottom)

中心点的屏幕坐标

overlay模式就是position,否则是世界坐标,需要worldtoscreen进行转换

private Vector3 GetSpacePos(RectTransform rect, Canvas canvas, Camera camera)
{
    if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
    {
        return rect.position;
    }
    return camera.WorldToScreenPoint(rect.position);

}
GetWorldCorners

返回四个角的世界坐标,对应的屏幕坐标依然和渲染模式有关

private void GetSpaceCorners(RectTransform rect, Canvas canvas, Vector3[] corners,Camera camera)
{
    if (camera == null)
    {
        camera = Camera.main;
    }
    rect.GetWorldCorners(corners);
    if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
    {

    }
    else
    {
        for (var i = 0; i < corners.Length; i++)
        {
            corners[i] = camera.WorldToScreenPoint(corners[i]);
        }
    }
}
RectTransformUtility.RectangleContainsScreenPoint

在overlay模式下不能用..

一些实际中可能用到的例子
获取鼠标点下图片的像素
public Rect GetSpaceRect(Canvas canvas, RectTransform rect, Camera camera)
{
    Rect spaceRect = rect.rect;
    Vector3 spacePos = GetSpacePos(rect, canvas, camera);
    //lossyScale
    spaceRect.x = spaceRect.x * rect.lossyScale.x + spacePos.x;
    spaceRect.y = spaceRect.y * rect.lossyScale.y + spacePos.y;
    spaceRect.width = spaceRect.width * rect.lossyScale.x;
    spaceRect.height = spaceRect.height * rect.lossyScale.y;
    return spaceRect;
}

public bool RectContainsScreenPoint(Vector3 point, Canvas canvas, RectTransform rect, Camera camera)
{
    if (canvas.renderMode != RenderMode.ScreenSpaceOverlay)
    {
        return RectTransformUtility.RectangleContainsScreenPoint(rect, point, camera);
    }

    return GetSpaceRect(canvas, rect, camera).Contains(point);
}

// Update is called once per frame
void Update()
{
    if (RectContainsScreenPoint(Input.mousePosition, _canvas, rect, _canvas.camera))
    {
        Image image = _uiObject.GetComponent<Image>();
        var spaceRect = GetSpaceRect(_canvas, rect, camera);
        var localPos = Input.mousePosition - new Vector3(spaceRect.x, spaceRect.y);
        var realPos = new Vector2(localPos.x , localPos.y );
        var imageToTextre = new Vector2(image.sprite.textureRect.width/spaceRect.width,
            image.sprite.textureRect.height/spaceRect.height);
        _resultImage.color = _uiObject.GetComponent<Image>().sprite.texture.GetPixel((int)(realPos.x*imageToTextre.x), (int)(realPos.y*imageToTextre.y));
    }
}

只在RenderMode是ScreenSpaceOverlay测试通过,主要过程涉及到坐标转换.

  1. 将图片的坐标转换到左下角为起点的坐标系 
  2. 将鼠标的屏幕坐标转换到相对于图片的坐标,并获得改坐标 
  3. 将该坐标换算到对应纹理的坐标点. 

几点发现

  1. lossyScale 是一个对象的全局缩放量,比如你给canvas加了一个CanvasScaler,那么image的缩放量会包括改值,localScale却不包括. 
  2. lossyScale 在Canvas的渲染模式是ScreenSpaceCamera时,数值略诡异,似乎和Camerasize有关,所以上述代码在ScreenSpaceCamera无效 
  3. RectTransform里面的rect返回的尺寸没有经过lossyScale,需要自己计算,详见GetSpaceRect函数 
  4. 计算鼠标相对于图片的坐标也要考虑lossyScale
  5. 最后计算出来的坐标是相对于图片的坐标,图片和内部的Texture之间的尺寸可能不是1:1的,所以也需要计算比例,参考imageToTextre变量 
总结

相对于flash,这次的ui系统最大的更新就是加入了四个锚点并且可以分开

吐槽 unity api文档写的真是烂,一定是程序员写了注释直接转过来的,还有开源一点诚意都木有,关键的RectTransform和Canvans之类的都没源码

Canvas的三种渲染模式的实际用处还有待发现,目前做重度UI相关的业务用ScreenSpaceOverlay一切正常,

渲染模式是WorldSpace的一个视频

<think>嗯,用户之前问过如何在Unity中获取Image组件的Rect信息,现在他们想进一步了解如何修改RectTransform。我需要结合之前的回答用户提供的引用内容来给出解决方案。首先,用户引用了关于CharacterController、iTween、协程Init方法的内容,但这些可能修改RectTransform关系不大,不过协程部分可能涉及到动画,或许可以提到用协程来平滑修改尺寸。 根据之前的回答,获取RectTransform主要是通过访问组件,而修改的话应该也是操作RectTransform的属性。用户可能遇到旋转或缩放后修改不生效的问题,需要提醒他们注意锚点轴心的设置。另外,引用[3]提到研究底层代码,可能需要解释RectTransform的工作原理,比如锚点偏移量的关系。 用户的需求是获取并修改,所以步骤应该分两部分:获取修改。可能需要包括直接修改尺寸、使用协程动画、处理布局影响等。还要注意不同坐标系的转换,比如世界坐标本地坐标的区别。引用中提到的iTween虽然不直接相关,但可以建议使用Dotween插件来做动画,或者用协程实现类似效果。 另外,用户之前的问题提到了常见问题,比如旋转后的准确性,所以在修改时也要考虑旋转缩放的影响。可能需要给出处理旋转后的实际尺寸的方法,比如通过顶点计算或者调整锚点。最后,相关问题可以引导用户了解更深入的内容,比如UI布局优化或动态调整的技巧。</think>根据Unity UI系统特性,修改Image组件的RectTransform需要从布局控制动态调整两个维度进行操作。以下是具体实现方案(基于你提供的引用内容历史问题): --- ### **一、基础RectTransform获取与修改** #### 1. **获取RectTransform组件** ```csharp using UnityEngine.UI; public class RectModifier : MonoBehaviour { private RectTransform _rt; private Image _image; void Start() { _image = GetComponent<Image>(); _rt = _image.rectTransform; // 核心访问方式[^2] } } ``` #### 2. **直接修改布局属性** ```csharp // 修改尺寸(考虑锚点影响) _rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 200f); _rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 100f); // 修改位置(基于锚点坐标系) _rt.anchoredPosition = new Vector2(50, -30); ``` --- ### **二、动态动画修改(结合协程技术[^1])** #### 1. 尺寸渐变动画 ```csharp IEnumerator AnimateWidth(float targetWidth, float duration) { float startWidth = _rt.rect.width; float timer = 0; while (timer < duration) { float newWidth = Mathf.Lerp(startWidth, targetWidth, timer/duration); _rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, newWidth); timer += Time.deltaTime; yield return null; // 协程帧控制[^1] } } // 启动动画 StartCoroutine(AnimateWidth(300f, 2f)); ``` #### 2. 位置弹性动画 ```csharp IEnumerator ElasticMove(Vector2 targetPos) { Vector2 startPos = _rt.anchoredPosition; float damp = 0.2f; while (Vector2.Distance(_rt.anchoredPosition, targetPos) > 0.1f) { _rt.anchoredPosition = Vector2.Lerp(_rt.anchoredPosition, targetPos, Time.deltaTime * 10); yield return null; } } ``` --- ### **三、高级修改技巧** #### 1. **锚点与轴心点控制** ```csharp // 修改锚点(影响子物体布局) _rt.anchorMin = new Vector2(0.5f, 0.5f); _rt.anchorMax = new Vector2(0.5f, 0.5f); // 修改轴心点(影响旋转/缩放中心) _rt.pivot = new Vector2(0, 1); // 左上角为轴心 ``` #### 2. **基于父物体的动态适配** ```csharp // 自动填充父容器 _rt.anchorMin = Vector2.zero; _rt.anchorMax = Vector2.one; _rt.offsetMin = new Vector2(10, 10); // 左/下边距 _rt.offsetMax = new Vector2(-10, -10); // 右/上边距 ``` --- ### **四、坐标系转换(重要)** ```csharp // 本地坐标转世界坐标 Vector3 worldPos = _rt.TransformPoint(_rt.rect.center); // 屏幕坐标转UI坐标(适合点击检测) RectTransformUtility.ScreenPointToLocalPointInRectangle( _rt, Input.mousePosition, Camera.main, out Vector2 localPoint ); ``` --- ### **五、常见问题解决方案** #### **Q1:修改尺寸后子物体布局异常** ```csharp // 强制刷新布局 LayoutRebuilder.ForceRebuildLayoutImmediate(_rt); ``` #### **Q2:旋转后的尺寸计算** ```csharp // 获取实际渲染尺寸(考虑旋转) Vector3[] corners = new Vector3[4]; _rt.GetWorldCorners(corners); float actualWidth = Vector3.Distance(corners[0], corners[3]); float actualHeight = Vector3.Distance(corners[0], corners[1]); ``` --- ### **六、性能优化建议** 1. 对频繁修改的RectTransform启用`Canvas.willRenderCanvases`事件监听 2. 使用`ContentSizeFitter`组件自动适配内容尺寸 3. 批量修改时使用`Canvas.BeginRenderOverlays``Canvas.EndRenderOverlays` --- ### **相关问题** 1. 如何实现UGUI的响应式布局? 2. RectTransform的锚点系统如何影响子物体布局? 3. 动态UI元素如何优化渲染性能? 4. 如何检测鼠标是否在Image的变形区域内?[^3] --- [^1]: 协程通过yield return实现帧控制,比Update更灵活 [^2]: RectTransform的尺寸修改必须考虑当前锚点配置 [^3]: 使用RectTransformUtility.RectangleContainsScreenPoint方法检测
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值