[UGUI]RectTransform设置锚点、轴心的问题和解决方案

目录

1、RectTransform和Debug

2、轴心(Pivot)

3、锚点(Anchors)

4、SizeDelta

5、代码

6、总结


首先大家有没有遇到过这样的问题,像下面这样在代码中直接给,轴心、锚点赋值。

anchors.x = 1;
anchors.y = 1;
rtf.pivot = new Vector2(1,1)

看起来是没有什么问题,但是实际会造成UI元素位置大小的改变,并不能达到我们单纯改变锚点、轴心的目的。但是这是为什么呢?

1、RectTransform和Debug

在Unity中除了Inspector面板上的属性,点击右上角的小箭头可以打开一个Debug模式,这里有组件更详细的属性。切换到Debug模式,看到的是这两组数据,现在虽然看起来是一样的,但是这只是一种特殊情况,本质上是不同的,RectTransform只是提供了一个可视化的编辑,实际控制UI元素的是右边的

 

当用代码改变Pivot ,AnchorMin,AnchorMax的时,是没有连带改变 AnchoredPosition和SizeDelta的,所以造成了图像位置大小的异常

 

2、轴心(Pivot)

轴心的改变只会影响AnchoredPosition的计算,而AnchoredPosition的值由锚点和轴心共同影响,具体计算方法为锚点到轴心的向量,因为涉及到锚点一会提到锚点再详细说明。

图中从锚点到轴心的向量就是AnchoredPosition啦

3、锚点(Anchors)

锚点是确定UI元素和它父物体的相对位置,计算方面既会影响到AnchoredPosition,也会影响到SizeDelta。锚点是由AnchorMin和AnchorMax控制,首先说下锚点的两种情况。

当Min和Max相等的时候为锚点:

当Min和Max不相等的时候为锚框:

 

而当处于锚框状态时,AnchoredPosition的值为锚框中的点P1与轴心P2的向量,P1是在锚框中对RectTransform的Pivot属性取插值得到的。可能不是很好理解我们举个例子这里RectTransform的Pivot为(0.75,0.75),P1为锚框坐标XY做0.75插值的位置,所以之前说的锚点状态只是锚框的一种特殊情况

4、SizeDelta

这个东西就比较奇怪了,有时候值是物体的宽高,有时候又是一组奇怪的数字。但是了解过后就能知道他的意义,也可以明白为什么Unity会给它起名叫做SizeDelta

亮公式

OffsetMin = LeftBottom - AnchoredMin

OffsetMax = RightTop - AnchoredMax

SizeDelta = OffsetMax - OffsetMin

乍一看这公式,就只知道AnchoredMin,AnchoredMax。其他的是个啥子东西嘛,这个不急我们先从这个Offset开始看,它是一个Vector2的变量,其实是可以直接从RectTransform里面获取到

其中的RightTop 和LeftBottom 则是代表UI元素右上和左下的坐标,AnchoredMin和AnchoredMax则代表锚框左下和右上的坐标,那求出来的是个什么呢?

先看这个锚点的情况

这个时候算出来的SizeDelta是这个样子,这个时候数值正好就是UI元素的宽高再看这个锚框的情况

可以看出Offset所表达的物理意义其实就是UI元素的边距离锚框的偏移量

而SizeDelta表示的是UI元素和他锚框区域的一个增减量的关系,可以理解成是UI元素的尺寸减去锚框的尺寸,比锚框小的时候就是负值,比锚框大就是正值

物体的实际尺寸则是用它的锚框尺寸加上它的SizeDelta,这也就是为什么设置成锚框可以做到自适应的效果

5、代码

既然知道了AnchoredPosition和SizeDelta的计算方法,那自然可以通过参数计算出正确的数值,我在这就朴实无华的把公式套了进去,大家仅做参考

 //轴心的
    private static void StayPosition(RectTransform rtf,Vector2 pivot){
        //计算物体轴心位置
        Vector3[] world_corners = new Vector3[4];
        rtf.GetWorldCorners(world_corners);
        float pivot_pos_x = Mathf.Lerp(world_corners[0].x,world_corners[2].x,pivot.x);
        float pivot_pos_y = Mathf.Lerp(world_corners[0].y,world_corners[2].y,pivot.y);
        Vector2 temp_pos = new Vector2(pivot_pos_x,pivot_pos_y);
        //计算锚框轴心位置
        Vector2[] anchors_world_pos = GetAnchorsWorldPosition(rtf);
        float anchor_x = Mathf.Lerp(anchors_world_pos[0].x,anchors_world_pos[1].x,pivot.x);
        float anchor_y = Mathf.Lerp(anchors_world_pos[0].y,anchors_world_pos[1].y,pivot.y);
        Vector2 anchor_pos = new Vector2(anchor_x,anchor_y);
        //求两点的向量
        rtf.anchoredPosition = temp_pos - anchor_pos;
    }

    //锚点的
    private static void StayPosition(RectTransform rtf,Vector2 anchor_min,Vector2 anchor_max){
        //设置anchoredPosition
        Vector2 pivot = rtf.pivot;
        Vector2[] anchors_world_pos = GetAnchorsWorldPosition(rtf,anchor_min,anchor_max);
        float anchor_x = Mathf.Lerp(anchors_world_pos[0].x,anchors_world_pos[1].x,pivot.x);
        float anchor_y = Mathf.Lerp(anchors_world_pos[0].y,anchors_world_pos[1].y,pivot.y);
        Vector2 anchor_pos = new Vector2(anchor_x,anchor_y);
        rtf.anchoredPosition = (Vector2)rtf.position - anchor_pos;
        //设置sizeDelta
        Vector3[] world_corners = new Vector3[4];
        rtf.GetWorldCorners(world_corners);
        Vector2 cur_offset_max = (Vector2)world_corners[2] - anchors_world_pos[1];
        Vector2 cur_offset_min = (Vector2)world_corners[0] - anchors_world_pos[0];
        Vector2 cur_size_delta = cur_offset_max - cur_offset_min;
        rtf.sizeDelta = cur_size_delta;
    }

 //获得rtf锚框位置的世界坐标
    private static Vector2[] GetAnchorsWorldPosition(RectTransform rtf,Vector2? anchor_min=null,Vector2? anchor_max=null){
        RectTransform praent_rtf = rtf.parent.GetComponent<RectTransform>();
        Vector3[] p_world_corners = new Vector3[4];
        praent_rtf.GetWorldCorners(p_world_corners);
        if(anchor_min == null) anchor_min = rtf.anchorMin;
        if(anchor_max == null) anchor_max = rtf.anchorMax;
        Vector2 min = (Vector2)p_world_corners[0]+(p_world_corners[2]-p_world_corners[0])*(Vector2)anchor_min;
        Vector2 max = (Vector2)p_world_corners[0]+(p_world_corners[2]-p_world_corners[0])*(Vector2)anchor_max;
        return new Vector2[]{min,max};
    }

6、总结

RectTransform是根据锚点和轴心,再配合AnchoredPosition和SizeDelta控制UI元素其相对父物体的位置和尺寸。但是单纯的改变锚点和轴心的位置不会一起改变这两个值,Unity里虽然有计算方法,但是封装在了Editor中不让我们直接用,我在这提供了一个简单的转换方法,但总觉得不是最优解。就当我抛砖引玉,希望得到大佬们的指点

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值