千家信息网

Unity ScrollRect怎么实现轨迹滑动效果

发表于:2024-11-26 作者:千家信息网编辑
千家信息网最后更新 2024年11月26日,这篇文章主要介绍"Unity ScrollRect怎么实现轨迹滑动效果",在日常操作中,相信很多人在Unity ScrollRect怎么实现轨迹滑动效果问题上存在疑惑,小编查阅了各式资料,整理出简单好
千家信息网最后更新 2024年11月26日Unity ScrollRect怎么实现轨迹滑动效果

这篇文章主要介绍"Unity ScrollRect怎么实现轨迹滑动效果",在日常操作中,相信很多人在Unity ScrollRect怎么实现轨迹滑动效果问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"Unity ScrollRect怎么实现轨迹滑动效果"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

以下内容是根据Unity 2020.1.01f版本进行编写的

1、目的

工作中遇到有需要实现轨迹滑动的滑动列表,通常的做法是计算贝塞尔曲线得出轨迹,但是我觉得计算贝塞尔曲线太麻烦了,或许有没有更简单的方法。

2、思考

轨迹滑动可以分两种情况:

第一种是轨迹滑动是比较简单的横向(或纵向)滑动,其中轨迹不会蜿蜒盘旋,也不涉及列表格子之间的重叠关系,这时可以分区间来对Y轴(或X轴)进行设置以达到格子沿着轨迹滑动的效果
例如,轨迹是这样的波浪型的,其中,轨迹点的数量可以无限增加,点越多轨迹可以设置得越平滑,但是消耗的性能也越多:

像这样,类似波浪型的轨迹,可以用一个Vector列表记录下每个点的位置和顺序,然后根据每个格子所在的位置,先由小到大循环判断在哪个区间,例如格子3在pos_5和pos_6中间,所以第三个格子的区间就是5,然后通过pos_5和pos_6两个点相减,得到一个从pos_5指向pos_6的向量vector,通过vector3.distance函数得到pos_5和pos_6两个点的横向距离distanceX1,然后再次通过vector3.distance函数得到pos_5与格子3得横向距离distanceX2,那么,格子3的位置 = pos_5的位置 + distanceX2 / distanceX1 * vector,得到位置后再把位置设置回去就可以了 

第二种就是更为复杂的轨迹滑动,例如蜿蜒盘旋式的轨迹,其中还包括有层级关系

例如,轨迹是蜿蜒盘旋的,同样是轨迹点越多,曲线越平滑,也越消耗性能:

像这样,蜿蜒盘旋的轨迹,就不能使用第一种方法了,因为有些位置,一个X值对应下来有多个Y值,无法区分当前需要的是哪个Y值,所以,需要使用另一种方法

我们可以通过获取所有的轨迹点,计算出每两个相邻轨迹点之间的距离,通过距离确定格子应该在哪个区间内

例如,格子3的位置是95,假设pos_1和pos_2的距离为50,pos_2和pos_3的距离为也是50,那么pos_1到pos_3的总距离就是100,所以格子3应该在区间pos_2和pos_3中间。接下来计算实际位置,因为此时格子的X轴和Y轴都会变化,同样无法使用第一种方法来计算格子的位置,需要通过格子的位置减去前面区间距离的和,在例子中,就是格子3的位置减去pos_1到pos_2的距离,即95 - 50 = 45,再通过剩余的距离除以当前区间的距离得出比例ratio,然后就是通过当前的两个区间点相减得到pos_2指向pos_3的向量vector,那么格子3的位置就是,pos_2的位置 + ratio * vector

最后,还有层级的问题,也就是哪个格子遮挡哪个格子,一般是后面的格子遮挡前面的格子,如果需要前面的遮挡后面的格子,只需要动态修改层级就行,unity提供了这个函数:Transform.SetAsFirstSibling()

在实际使用中,我发现使用这种方法后,content的长度有时候会过长,有时候会过短,所以需要确定好格子的数量,设置好content的正确长度

注意:以上都是最基础的思想,因为滑动时改变的是content节点,格子的位置实际上是不变的,或者是由代码设置的,所以实际代码中还需要考虑scrollrect的宽高以及content节点的滑动值(即posX值,滑动时此值会变化)

3、自定义实现轨迹滑动

using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;[RequireComponent(typeof(ScrollRect))]public class CurveSliding : MonoBehaviour{    public Transform posListRoot;    public float contentwidth;              //必须自己设置的content实际长度    public bool isChangeHierarchy = false;    private ScrollRect scrollRect;    private RectTransform content;    private List posTransformList = new List();    private List item_transformList = new List();    private List posX_List = new List();    private List distanceList = new List();    private float scrollRectWidth;    private float scrollRectHeight;    private float posTotalDistance;    private float spacing = 0;    private void Start()    {        scrollRect = GetComponent();        content = scrollRect.content.transform as RectTransform;        HorizontalLayoutGroup contentLayoutGroup = content.GetComponentsInChildren(true)[0];        if(contentLayoutGroup != null && contentLayoutGroup.name == content.name)        {            spacing = contentLayoutGroup.spacing;        }        scrollRectWidth = scrollRect.GetComponent().sizeDelta.x;        scrollRectHeight = scrollRect.GetComponent().sizeDelta.y;        for (int i = 0; i < content.childCount; i++)        {            RectTransform item_transform = content.GetChild(i) as RectTransform;            if (item_transform.gameObject.activeInHierarchy)            {                item_transformList.Add(item_transform);                posX_List.Add(item_transform.localPosition.x);            }            else            {                continue;            }        }        float totalDistance = 0;        for(int i = 0;i < posListRoot.childCount;i++)        {            Transform posTransform = posListRoot.GetChild(i);            if (i > 0)            {                Transform previousPosTransform = posListRoot.GetChild(i - 1);                totalDistance += Vector3.Distance(posTransform.localPosition, previousPosTransform.localPosition);            }            posTransformList.Add(posTransform);            distanceList.Add(totalDistance);        }        posTotalDistance = distanceList[distanceList.Count - 1];        content.sizeDelta = new Vector2(contentwidth, content.sizeDelta.y);        OnValueChange(Vector2.zero);        scrollRect.onValueChanged.AddListener(OnValueChange);    }    public void OnValueChange(Vector2 vector)    {        for(int i = 0;i < item_transformList.Count;i++)        {            float localPosX = posX_List[i];            float posX = localPosX + content.anchoredPosition.x;            //如果当前节点的位置 - content的X轴偏移值 > 滑动列表的宽度,则说明当前item在可视范围外            if (posX > posTotalDistance + 200 || posX < 0)            {                continue;            }            int index = -1;            foreach(var totalDistance in distanceList)            {                if(posX < totalDistance)                {                    break;                }                else                {                    index++;                }            }            //如果index+1小于位置列表的数量,则其在位置区间内,否则应该在位置区间外            if (index + 1 < posListRoot.childCount && index + 1 > 0)            {                float ratio = (posX - distanceList[index]) / (distanceList[index + 1] - distanceList[index]);                Vector3 newPos = posTransformList[index].localPosition - ratio * (posTransformList[index].localPosition - posTransformList[index + 1].localPosition);                item_transformList[i].localPosition = new Vector3(newPos.x + scrollRectWidth/2 - content.anchoredPosition.x, -scrollRectHeight / 2 + newPos.y, 0);            }            else if(index <= -1)            {                item_transformList[i].localPosition = new Vector3(item_transformList[i].localPosition.x, -scrollRectHeight / 2 + posListRoot.GetChild(0).localPosition.y, 0);            }            else if(index >= posListRoot.childCount - 1)            {                if (i < 1)                {                    continue;                }                RectTransform previousItem_RectTransform = item_transformList[i - 1];                item_transformList[i].localPosition = new Vector3(previousItem_RectTransform.localPosition.x + spacing + previousItem_RectTransform.sizeDelta.x/2 + item_transformList[i].sizeDelta.x/2, -scrollRectHeight / 2 + posListRoot.GetChild(posListRoot.childCount - 1).localPosition.y, 0);            }            if (isChangeHierarchy)            {                item_transformList[i].SetAsFirstSibling();            }        }    }}

4、两种方法的优缺点

1)、 两种方法都不能使用排序组件,因为排序组件会控死格子的位置,通过代码也无法修改
2)、 第二种方法比第一种方法更复杂,同时消耗的性能也更多,但是能实现更复杂的效果
3)、第二种方法需要设置conten的长度,即格子的数量无法动态变化,是一大缺点

5、最终效果

第一种:

第二种:

到此,关于"Unity ScrollRect怎么实现轨迹滑动效果"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0