Unity ScrollRect控件,将Content中任意元素定位到viewport中心的方法

参考:

ugui max min auchor解释

localPosition与anchoredPosition的转化关系

Unity UGUI 原理篇(三):RectTransform

RectTransform中localPosition与anchoredPosition的区别

UGUI在两个UI间坐标转换


这个需求是做一个小游戏项目中,需要将玩家退出游戏时的关卡保存,在重新加载的过程中,在选关界面的中心自动定位到该关卡。

一开始我的思路也很简单:获取该元素的世界坐标,将使用viewport(视口)的中心坐标减去该坐标,再调用Dotween的DoMoveY()方法,将content平移到该点。

但过程中出了些意外,原因是我的关卡都是通过 Instantiate(Prefab)后填充到Content中,但由于我将自动定位的方法直接写到了awake()中,导致每次都拿到了Prefab的世界坐标;而不是生成后的最新坐标,此时我以为我之前的思路出了问题,于是开始寻找其他坐标转换的方法。

事实上最开始的方法是完全可行且非常简洁的,在重新弄明白localPosition与anchoredPosition的区别后,我想到了新的解决办法:

因为当子物体的anchor与父物体的pivot坐标重合时anchoredPosition3D与localPosition相等,而Content的父物体是ViewPort ,所以我需要将content下的元素父物体改为Viewport,这样两者的anchoredPosition才在同一坐标系,之后的思路和前面一致,只不过操作对象由Tranform.position改为了anchoredPosition。

实际检测以上两种方法都可行,一下是我的代码,实际上项目中我删除了一些控件,代码中的ScrollRect你可以视为原本的viewport控件,而Viewport你可以视为Content( \( ̄▽ ̄)/)

//世界坐标移动的方法
 void Test(GameObject selected)
    {

        var target = selected.GetComponent<RectTransform>();

        Vector3 sCenter = scrollRect.transform.position;
        Debug.Log("Center Pos: " + sCenter);

        Vector3 itemCenterPos = target.transform.position;
        Debug.Log("Item Center Pos: " + itemCenterPos);

        Vector3 difference = sCenter - itemCenterPos;
        Vector3 newPos = viewport.transform.position + difference;

        viewport.transform.DOMoveY(newPos.y, 1f);
    }
//anchoredPosition的移动方法
    void Test2(GameObject selected)
    {
        var target = selected.GetComponent<RectTransform>();
        Text text = target.transform.GetChild(0).GetComponent<Text>();
        Debug.Log("当前物体ID" + text.text);
        Debug.Log("转换前物体坐标" + target.anchoredPosition);
        var screenPoint = RectTransformUtility.WorldToScreenPoint(camera, target.transform.position);
        //将物体作为scollrect的子物体
        Vector2 rTarget;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(scrollRect, screenPoint, camera, out  rTarget);

        Debug.Log("转换后物体坐标" + rTarget);
        Vector2 sCenter = scrollRect.rect.center;
        Debug.Log("scrollRect中心坐标" + sCenter);

        Vector2 distance = sCenter - rTarget;

        Vector2 newPos = viewport.anchoredPosition + distance;

        viewport.DOAnchorPosY (newPos.y, 2f);

    }

效果图:

在这里插入图片描述

要判断ScrollRectContent里面的元素是否在可视范围内,可以使用RectTransform组件和Viewport的位置信息来进行判断。可以按照以下步骤进行判断: 1. 获取ScrollRect组件和ContentRectTransform组件: ```csharp ScrollRect scrollRect = yourScrollRect.GetComponent<ScrollRect>(); RectTransform contentRectTransform = scrollRect.content.GetComponent<RectTransform>(); ``` 2. 获取Viewport的位置信息: ```csharp RectTransform viewportRectTransform = scrollRect.viewport.GetComponent<RectTransform>(); Vector3[] viewportCorners = new Vector3[4]; viewportRectTransform.GetWorldCorners(viewportCorners); Vector2 viewportMin = viewportCorners[0]; Vector2 viewportMax = viewportCorners[2]; ``` 3. 获取Content下每个元素的位置信息,并判断是否在可视范围内: ```csharp for (int i = 0; i < contentRectTransform.childCount; i++) { RectTransform childRectTransform = contentRectTransform.GetChild(i).GetComponent<RectTransform>(); Vector3[] childCorners = new Vector3[4]; childRectTransform.GetWorldCorners(childCorners); Vector2 childMin = childCorners[0]; Vector2 childMax = childCorners[2]; bool isElementVisible = childMax.x >= viewportMin.x && childMin.x <= viewportMax.x && childMax.y >= viewportMin.y && childMin.y <= viewportMax.y; // 根据需要使用isElementVisible进行进一步的操作 } ``` 通过遍历Content下的每个元素,获取其位置信息,并与Viewport的位置信息进行比较,可以判断元素是否在可视范围内。根据需要,可以在判断后对元素进行进一步的操作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值