欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Unity ScrollRect实现轨迹滑动效果

程序员文章站 2022-03-03 08:24:59
本文实例为大家分享了unity scrollrect实现轨迹滑动效果的具体代码,供大家参考,具体内容如下以下内容是根据unity 2020.1.01f版本进行编写的1、目的工作中遇到有需要实现轨迹滑动...

本文实例为大家分享了unity scrollrect实现轨迹滑动效果的具体代码,供大家参考,具体内容如下

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

1、目的

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

2、思考

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

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

Unity ScrollRect实现轨迹滑动效果

像这样,类似波浪型的轨迹,可以用一个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,得到位置后再把位置设置回去就可以了 

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

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

Unity ScrollRect实现轨迹滑动效果

像这样,蜿蜒盘旋的轨迹,就不能使用第一种方法了,因为有些位置,一个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<transform> postransformlist = new list<transform>();
    private list<recttransform> item_transformlist = new list<recttransform>();
    private list<float> posx_list = new list<float>();
    private list<float> distancelist = new list<float>();
    private float scrollrectwidth;
    private float scrollrectheight;
    private float postotaldistance;
    private float spacing = 0;

    private void start()
    {
        scrollrect = getcomponent<scrollrect>();
        content = scrollrect.content.transform as recttransform;
        horizontallayoutgroup contentlayoutgroup = content.getcomponentsinchildren<horizontallayoutgroup>(true)[0];
        if(contentlayoutgroup != null && contentlayoutgroup.name == content.name)
        {
            spacing = contentlayoutgroup.spacing;
        }
        scrollrectwidth = scrollrect.getcomponent<recttransform>().sizedelta.x;
        scrollrectheight = scrollrect.getcomponent<recttransform>().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实现轨迹滑动效果

第二种:

Unity ScrollRect实现轨迹滑动效果

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

相关标签: Unity 轨迹滑动