Unity ScrollView实现无限循环效果

本文实例为大家分享了Unity ScrollView实现无限循环效果的具体代码,供大家参考,具体内容如下

在Unity引擎中ScrollView组件是一个使用率比较高的组件,该组件能上下或者左右拖动的UI列表,背包、展示多个按钮等情况的时候会用到,在做排行榜类似界面时,item非常多,可能有几百个,一次创建这么多GameObject是非常卡的。为此,使用只创建可视区一共显示的个数,加上后置准备个数。

由于ScrollView有两种滚动方式,水平滚动或者垂直滚动,所以我创建了ScrollBase基类,和HorizontalScroll子类及VerticalScroll子类,在父类中设定了公共的抽象方法OnDrawView—绘制显示区的方法;CreateItem----创建Item的方法;Update----滚动状态下实时更新Item的位置。

ScrollBase基类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public enum MoveType
{
    UP,
    DOWN,
    LEFT,
    RIGHT,
    STOP
}

public abstract class ScrollBase
{

    public ItemArray<ItemData> items;
    public ScrollViewLoop scrollLoop;
    public float viewHeight;
    public float viewWidth;
    public float contentHeight;
    public float contentWidth;
    public RectTransform rectTransform;
    public ScrollRect scrollRect;
    public MoveType type;

    public float item_width;//每个Item的宽度
    public float item_height;//每个Item的高度
    public int viewNum;//显示区显示的个数
    public int addNum;//后置准备个数
    public int itemNum;//总共需要多少Item的滚动
    public float spaceY;//垂直Item间隔
    public float spaceX;//水平Item间隔

    public ScrollBase(ScrollViewLoop _scrollLoop, bool isCreateItem = true)
    {
        scrollLoop = _scrollLoop;
        rectTransform = scrollLoop.GetComponent<RectTransform>();
        scrollRect = scrollLoop.GetComponent<ScrollRect>();
        if (!scrollRect)
        {
            scrollRect = scrollLoop.gameObject.AddComponent<ScrollRect>();
        }
        item_width = scrollLoop.item_width;
        item_height = scrollLoop.item_height;
        viewNum = scrollLoop.viewNum;
        addNum = scrollLoop.addNum;
        itemNum = scrollLoop.itemNum;
        spaceY = scrollLoop.spaceY;
        spaceX = scrollLoop.spaceX;

        OnDrawView();
        if (isCreateItem)
        {
            CreateItem();
        }

    }

    public abstract void OnDrawView();

    public abstract void CreateItem();

    public abstract void Update();

}

ScrollViewLoop控制类

这个属性类需要挂载在ScrollRect物体身上,然后在面板上进行属性配置

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ItemData
{
    public RectTransform itemRect;
    public int index;
}

public class ScrollViewLoop : MonoBehaviour
{
    public GameObject itemPrefab;
    [Header("每个Item的宽度")]
    public float item_width;
    [Header("每个Item的高度")]
    public float item_height;
    [Header("显示区显示几个Item")]
    public int viewNum = 6;
    [Header("显示区外多复制几个Item")]
    public int addNum = 2;
    [Header("总共需要多少Item")]
    public int itemNum = 100;
    [Header("Item之间的垂直距离")]
    public float spaceY = 2;
    public float spaceX = 2;
    public delegate void ItemChange(ItemData itemData);
    public event ItemChange OnChange;//Item位置改变时调用的事件
    [Header("是否是横向滑动")]
    public bool isHorizontal = false;
    ScrollBase scrollBase;

    void Start()
    {

        if (isHorizontal)
        {
            scrollBase = new HorizontalScroll(this);
        }
        else
        {
            scrollBase = new VerticalScroll(this);
        }

    }

    public void UseOnChange(ItemData itemData)
    {
        OnChange?.Invoke(itemData);
    }

    //ScrollRect滚动
    void Update()
    {
        if (scrollBase != null)
        {
            scrollBase.Update();
        }
    }

}

ItemArray

我们实现无限循环的核心思想是,当滚动框向左移动时,就判断左边的第一个Item距离显示框左边框的距离,是否大于Item水平间隔*0.5+Item的宽度,如果大于则把第一个Item放到最后的位置,当滚动框向右移动时就判断右边的Item距离右边框的距离情况,这样我们就可以在显示区域一直看到有Item的规则显示,所以我定制了一个泛型容器类,这个类存放所有的Item信息,当我们取第一个Item时自动把第一个Item放到最后,当我们取最后一个Item时自动把最后的一个Item放到最前面。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Item集合类
/// </summary>
/// <typeparam name="T"></typeparam>
public class ItemArray<T> where T : ItemData
{
    List<T> list;
    public int count
    {
        get
        {
            return list.Count;
        }
    }

    public ItemArray()
    {
        list = new List<T>();
    }

    public ItemArray(T[] ts)
    {
        if (ts == null)
        {
            return;
        }
        for (int i = 0; i < ts.Length; i++)
        {
            list.Add(ts[i]);
        }
    }

    /// <summary>
    /// 添加一个元素
    /// </summary>
    /// <param name="t"></param>
    public void AddItem(T t)
    {
        list.Add(t);
    }

    public T GetFirst()
    {
        if (list.Count == 0)
        {
            return default(T);
        }
        T t = list[0];
        list.RemoveAt(0);
        t.index = list[list.Count - 1].index + 1;
        list.Add(t);
        return t;
    }

    public T GetLast()
    {
        if (list.Count == 0)
        {
            return default(T);
        }
        T t = list[list.Count - 1];
        list.RemoveAt(list.Count - 1);
        t.index = list[0].index - 1;
        list.Insert(0, t);
        return t;
    }

    public T[] GetArray()
    {
        T[] ts = new T[list.Count];
        for (int i = 0; i < list.Count; i++)
        {
            ts[i] = list[i];
        }
        return ts;
    }

    public T this[int index]
    {
        get
        {
            return list[index];
        }
    }

}

HorizontalScroll子类及VerticalScroll子类

ScrollView无限循环的核心功能代码都在这两个子类中

HorizontalScroll子类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HorizontalScroll : ScrollBase
{
    public HorizontalScroll(ScrollViewLoop _scrollLoop, bool isCreateItem = true) : base(_scrollLoop, isCreateItem)
    {
        scrollRect.horizontal = true;
        scrollRect.vertical = false;
    }

    public override void CreateItem()
    {
        items = new ItemArray<ItemData>();
        for (int i = 0; i < viewNum + addNum; i++)
        {
            GameObject item = GameObject.Instantiate(scrollLoop.itemPrefab);
            RectTransform itemRect = item.GetComponent<RectTransform>();
            ItemData data = new ItemData();
            data.itemRect = itemRect;
            data.index = i;
            items.AddItem(data);
            scrollLoop.UseOnChange(data);
            itemRect.sizeDelta = new Vector2(item_width, item_height);
            itemRect.anchorMin = new Vector2(0, 1);
            itemRect.anchorMax = new Vector2(0, 1);
            itemRect.pivot = new Vector2(0, 1);
            itemRect.localScale = Vector2.one;
            item.transform.parent = scrollRect.content;
            item.transform.localPosition = new Vector3(i * (item_width + spaceX), 0, 0);
        }
    }

    void RewindItemPos()
    {
        int index = Mathf.FloorToInt(Mathf.Abs(scrollRect.content.localPosition.x) / (item_width + spaceX));
        for (int i = 0; i < items.count; i++)
        {
            RectTransform itemRect = items[i].itemRect;
            items[i].index = index;
            scrollLoop.UseOnChange(items[i]);
            itemRect.transform.localPosition = new Vector3(items[i].index * (item_width + spaceX), 0, 0);
            index++;
        }
    }

    public override void OnDrawView()
    {
        SetRectTransform(rectTransform);
        rectTransform.sizeDelta = new Vector2(item_width * viewNum + (viewNum - 1) * spaceX, item_height);
        SetRectTransform(scrollRect.viewport);
        viewHeight = scrollLoop.item_height;
        viewWidth = viewNum * item_width + (viewNum - 1) * spaceX;
        scrollRect.viewport.sizeDelta = new Vector2(viewWidth, viewHeight);
        scrollRect.viewport.localPosition = Vector3.zero;
        SetRectTransform(scrollRect.content);
        contentHeight = item_height;
        contentWidth = itemNum * item_width + (itemNum - 1) * spaceX;
        scrollRect.content.sizeDelta = new Vector2(contentWidth, contentHeight);
        scrollRect.content.localPosition = Vector3.zero;
    }

    public void SetRectTransform(RectTransform rect)
    {
        rect.anchorMin = new Vector2(0, 0.5f);
        rect.anchorMax = new Vector2(0, 0.5f);
        rect.pivot = new Vector2(0, 1);
        rect.localScale = Vector2.one;
    }

    float lastX = 0;
    MoveType lastMoveType = MoveType.STOP;
    public override void Update()
    {
        float x = scrollRect.content.localPosition.x;
        if (x > lastX)
        {
            type = MoveType.RIGHT;
        }
        else if (x < lastX)
        {
            type = MoveType.LEFT;
        }
        else
        {
            type = MoveType.STOP;
            if (lastMoveType != type)
            {
                Debug.Log("重置位置");
                RewindItemPos();
            }
        }
        if (type == MoveType.LEFT)//向左滑动
        {
            if (items[items.count - 1].index == itemNum - 1)
            {
                return;
            }
            float firstItemX = items[0].itemRect.localPosition.x;
            if (-x - firstItemX - item_width >= spaceX * 0.5f)
            {
                float lastItemX = items[items.count - 1].itemRect.localPosition.x;
                ItemData firstItem = items.GetFirst();
                firstItem.itemRect.localPosition = new Vector3(lastItemX + spaceX + item_width, 0, 0);
                if (firstItem.index < itemNum)
                {
                    scrollLoop.UseOnChange(firstItem);
                }
            }
        }
        else if (type == MoveType.RIGHT)//向右滑动
        {
            if (items[0].index == 0)
            {
                return;
            }
            float lastItemX = items[items.count - 1].itemRect.localPosition.x;
            if (lastItemX + x - viewWidth >= spaceX * 0.5f)
            {
                float firstItemX = items[0].itemRect.localPosition.x;
                ItemData lastItem = items.GetLast();
                lastItem.itemRect.localPosition = new Vector3(firstItemX - spaceX - item_width, 0, 0);
                if (lastItem.index >= 0)
                {
                    scrollLoop.UseOnChange(lastItem);
                }
            }
        }
        lastMoveType = type;
        lastX = x;
    }
}

VerticalScroll子类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class VerticalScroll : ScrollBase
{

    public VerticalScroll(ScrollViewLoop _scrollLoop, bool isCreateItem = true) : base(_scrollLoop, isCreateItem)
    {
        scrollRect.horizontal = false;
        scrollRect.vertical = true;
    }

    public override void CreateItem()
    {
        items = new ItemArray<ItemData>();
        for (int i = 0; i < viewNum + addNum; i++)
        {
            GameObject item = GameObject.Instantiate(scrollLoop.itemPrefab);
            RectTransform itemRect = item.GetComponent<RectTransform>();
            ItemData data = new ItemData();
            data.itemRect = itemRect;
            data.index = i;
            items.AddItem(data);
            scrollLoop.UseOnChange(data);
            itemRect.sizeDelta = new Vector2(item_width, item_height);
            itemRect.anchorMin = new Vector2(0, 1);
            itemRect.anchorMax = new Vector2(0, 1);
            itemRect.pivot = new Vector2(0, 1);
            itemRect.localScale = Vector2.one;
            item.transform.parent = scrollRect.content;
            item.transform.localPosition = new Vector3(0, (-i) * (item_height + spaceY), 0);
        }
    }

    void RewindItemPos()
    {
        int index = Mathf.FloorToInt(Mathf.Abs(scrollRect.content.localPosition.y) / (item_height + spaceY));
        for (int i = 0; i < items.count; i++)
        {
            RectTransform itemRect = items[i].itemRect;
            items[i].index = index;
            scrollLoop.UseOnChange(items[i]);
            itemRect.transform.localPosition = new Vector3(0, (-items[i].index) * (item_height + spaceY), 0);
            index++;
        }
    }

    public override void OnDrawView()
    {
        SetRectTransform(rectTransform);
        rectTransform.sizeDelta = new Vector2(item_width, viewNum * item_height + (viewNum - 1) * spaceY);
        SetRectTransform(scrollRect.viewport);
        viewHeight = viewNum * item_height + (viewNum - 1) * spaceY;
        scrollRect.viewport.sizeDelta = new Vector2(item_width, viewHeight);
        scrollRect.viewport.localPosition = Vector3.zero;
        SetRectTransform(scrollRect.content);
        contentHeight = itemNum * item_height + (itemNum - 1) * spaceY;
        scrollRect.content.sizeDelta = new Vector2(item_width, contentHeight);
        scrollRect.content.localPosition = Vector3.zero;
    }

    public void SetRectTransform(RectTransform rect)
    {
        rect.anchorMin = new Vector2(0.5f, 1);
        rect.anchorMax = new Vector2(0.5f, 1);
        rect.pivot = new Vector2(0, 1);
        rect.localScale = Vector2.one;
    }

    float lastY = 0;
    MoveType lastMoveType = MoveType.STOP;
    public override void Update()
    {
        float y = scrollRect.content.localPosition.y;
        if (y > lastY)
        {
            type = MoveType.UP;
        }
        else if (y < lastY)
        {
            type = MoveType.DOWN;
        }
        else
        {
            type = MoveType.STOP;
            if (lastMoveType != type)
            {
                RewindItemPos();
            }
        }
        if (type == MoveType.UP)//向上滑动
        {

            if (items[items.count - 1].index == itemNum - 1)
            {
                return;
            }
            float firstItemY = items[0].itemRect.localPosition.y;
            if (firstItemY - item_height + y >= spaceY * 0.5f)
            {
                float lastItemY = items[items.count - 1].itemRect.localPosition.y;
                ItemData firstItem = items.GetFirst();
                firstItem.itemRect.localPosition = new Vector3(0, lastItemY - spaceY - item_height, 0);
                if (firstItem.index < itemNum)
                {
                    scrollLoop.UseOnChange(firstItem);
                }
            }
        }
        else if (type == MoveType.DOWN)//向下滑动
        {
            if (items[0].index == 0)
            {
                return;
            }
            float lastItemY = items[items.count - 1].itemRect.localPosition.y;
            if (-lastItemY - y - viewHeight >= spaceY * 0.5f)
            {
                float firstItemY = items[0].itemRect.localPosition.y;
                ItemData lastItem = items.GetLast();
                lastItem.itemRect.localPosition = new Vector3(0, firstItemY + spaceY + item_height, 0);
                if (lastItem.index >= 0)
                {
                    scrollLoop.UseOnChange(lastItem);
                }
            }

        }
        lastMoveType = type;
        lastY = y;
    }
}

编辑器拓展

这一步我们进行编辑器的拓展,根据ScrollViewLoop控制类面板上的数据我们在未运行的状态下改变ScrollView的显示区域,这样更利于我们的可视化排版。

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

[CustomEditor(typeof(ScrollViewLoop))]
public class ScrollLopEditor : Editor
{
    ScrollViewLoop scrollLoop;
    private RectTransform scrollRectTransfrom;
    private ScrollRect scrollRect;
    private void OnEnable()
    {
        scrollLoop = target as ScrollViewLoop;
        scrollRectTransfrom = scrollLoop.GetComponent<RectTransform>();
        scrollRect = scrollLoop.GetComponent<ScrollRect>();

    }

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        bool isBtn = GUILayout.Button("编辑ScrollView显示区大小");
        if (isBtn)
        {
            if (scrollLoop.isHorizontal)
            {
                ScrollBase scrollBase = new HorizontalScroll(scrollLoop, false);
            }
            else
            {
                ScrollBase scrollBase = new VerticalScroll(scrollLoop, false);
            }
        }
    }

}

测试

测试一下功能效果

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    public ScrollViewLoop sl;

    private void Start()
    {
        sl.OnChange += Sl_OnChange;
    }

    //Item位置改变时自动改变Item展示内容
    private void Sl_OnChange(ItemData itemData)
    {
        itemData.itemRect.GetChild(0).GetComponent<Text>().text = "ITEM"+itemData.index;
    }
}

面板显示如下:

运行效果如下(本来想放一段视频的,遗憾的是没能成功操作,尴尬):

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

(0)

相关推荐

  • Unity3D实现待机状态图片循环淡入淡出

    本文实例为大家分享了Unity3D实现图片循环淡入淡出的具体代码,供大家参考,具体内容如下 1.说明 由于近期项目需求,在网上找了淡入淡出的代码,但都是两张图片淡入淡出结束后就没有了(可能我没找到其他好的作品).所以我做了简单的扩展 2.简单思路 既然待机状态下的图片淡入淡出切换,所以,首先要判断何时为待机状态(即屏幕没有任何操作的情况下):其次,图片静止一段时间后,开始淡入淡出,第一张图片淡入,第二张淡出:接着图片再次静止一段时间,然后接着下次的淡入淡出,但因为是循环淡入淡出,所以要考虑重新加

  • Unity实现3D循环滚动效果

    本文实例为大家分享了Unity实现3D循环滚动效果展示的具体代码,供大家参考,具体内容如下 然后通过SetDepthAndPosition这个方法,实现图片的空间空间展开 Z轴和Y轴,系数是一样的 经过上面设置,空间就摆开了 using UnityEngine; using System.Collections; using System.Collections.Generic; public class SelectRole : MonoBehaviour { public GameObjec

  • Unity3D UGUI实现缩放循环拖动卡牌展示效果

    本文实例为大家分享了Unity3D UGUI实现缩放循环拖动卡牌展示的具体代码,供大家参考,具体内容如下 需求:游戏中展示卡牌这种效果也是蛮酷炫并且使用的一种常见效果,下面我们就来实现以下这个效果是如何实现. 思考:第一看看到这个效果,我们首先会想到UGUI里面的ScrollRect,当然也可以用ScrollRect来实现缩短ContentSize的width来自动实现重叠效果,然后中间左右的卡牌通过计算来显示缩放,这里我并没有用这种思路来实现,我提供另外一种思路,就是自己去计算当前每个卡牌的位

  • Unity ScrollView实现无限循环效果

    本文实例为大家分享了Unity ScrollView实现无限循环效果的具体代码,供大家参考,具体内容如下 在Unity引擎中ScrollView组件是一个使用率比较高的组件,该组件能上下或者左右拖动的UI列表,背包.展示多个按钮等情况的时候会用到,在做排行榜类似界面时,item非常多,可能有几百个,一次创建这么多GameObject是非常卡的.为此,使用只创建可视区一共显示的个数,加上后置准备个数. 由于ScrollView有两种滚动方式,水平滚动或者垂直滚动,所以我创建了ScrollBase基

  • Unity ScrollView实现无限滑动效果

    本文实例为大家分享了Unity ScrollView实现无限滑动效果的具体代码,供大家参考,具体内容如下 一.效果演示 二.前言 当邮件中有1000封邮件,商店列表中有1000个物体,如果直接实例化1000条数据显示则会大大增加DrawCall,而大量不可见的数据被Mask组件排除在可视范围之外,但他们依然存在,这时就需要考虑通过一个无限滑动的ScrollView来优化渲染性能 三.实现思路 通过头下标和尾下标记录当前实例化数据的最大最小索引,之后用Content的锚点位置与当头下标的锚点位置进

  • IOS实现图片轮播无限循环效果

    本文接着上篇文章进行叙述讲解,主要为大家分享了图片轮播无限循环效果的实现方法,具体内容如下 之前说到第一个问题,ScrollView移动到最后一张图片时无法移动了,这是因为ScrollView已经移动到最后,而图片又是依次排列,自然也就无法移动. 解决办法是,我们换一个思路实现图片轮播效果,ScrollView上只放三个ImageView,屏幕始终显示中间的ImageView,左边和右边的ImageView分别代表前一张图片和后一张图片,屏幕移动的时候,中间的ImageView变化,同时左右两边

  • Android 实现通知消息水平播放、无限循环效果

    今天我们来实现一个简单的效果,通知消息无限循环播放,先看效果图: 这个效果也很常见,实现的方法也有很多,我是使用RecyclerView来实现的,觉得还是挺不错的,就写下来分享给大家. 下面先看我们的布局文件main.xml,里面主要是一个RecyclerView: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.andr

  • Android实现ViewPager无限循环效果(二)

    本文实例为大家分享了Android实现ViewPager无限循环效果的第二种方式,供大家参考,具体内容如下 原理:在Adapter中将getCount设置为无限大 package com.xiaomai.myproject.demo; import android.os.Bundle; import android.support.v4.view.ViewPager; import android.view.ViewGroup; import android.widget.ImageView;

  • Unity ScrollView实现自动吸附效果

    本文实例为大家分享了Unity ScrollView实现自动吸附效果的具体代码,供大家参考,具体内容如下 一.效果演示 二.实现思路 通过使用UGUI的拖拽接口,在拖拽结束时比较当前滑动框的NormalizedPositon与每一页的NormalizedPositon值,找到距离当前拖拽结束位置最近的页并缓慢滑动过去 三.使用说明 --此功能脚本是对ScrollView的扩展,所以必须添加UGUI提供的基础Scroll View --Content上必须添加GridLayoutGroup组件并添

  • Android TV 3D卡片无限循环效果

    TV 3D卡片无限循环效果,供大家参考,具体内容如下 ##前言 1.需求:实现3个卡片实现无限循环效果:1-2-3-1-2-3-1-,而且要实现3D效果:中间突出,两侧呈角度显示 2.Viewpager实现方式 (1) LoopViewpager,有兴趣的同学可以去github上看一下. (2) 通过定义一个item的个数Integer,MAX,然后设置初始位置为:Integer,MAX/2. 以上方式如果简单的加载图片这种方式还可取,由于需求3个界面内部控件比较多,在加上需要实现自定义的的3D

  • Android ViewPager实现无限循环效果

    最近项目里有用到ViewPager来做广告运营位展示,看到现在很多APP的广告运营位都是无限循环的,所以就研究了一下这个功能的实现. 先看看效果 从一个方向上一直滑动,么有滑到尽头的感觉,具体是怎么实现的呢?看下面的思路. 实现思路 此处画了一幅图来表达实现无限循环的思路,即在数据起始位置前插入最后一项数据,在最后一项数据后插入第一项数据,当滑动到此处时,更新页面的索引位置就ok了 . 代码实现 这个方法用于数据处理,其中mediaList是原始数据,newMediaList是处理完的数据,mM

  • Android ViewPager导航小圆点实现无限循环效果

    之前用View Pager做了一个图片切换的推荐栏(就类似与淘宝.头条客户端顶端的推荐信息栏),利用View Pager很快就能实现,但是一次无意间使用淘宝APP的时候,突然发现它的效果和我做的还不一样,淘宝APP的推荐栏可以左右无限循环切换,而ViewPager自身其实并没有支持这个功能. 其实实现这个无限循环不难,只需要在数据源的首尾各添加一张多余的图片,在onPagerChangeListener()中监听position<1和position>(总数据条目-1)就可以了.另外一点需要注

  • Android实现ViewPager无限循环效果(一)

    本文实例为大家分享了Android实现ViewPager无限循环的具体代码,供大家参考,具体内容如下 方式一: 实现原理: 假设有3张图片,分别是1,2,3,那么就创建5张图片,这5张图片的顺序为:3,1,2,3,1,其中1,2,3为我们要实现滑动的图片,最左面的3和最右面的1是我们另外添加的图片,开始时,显示图片1,当图片向左滑动依次为1,2,3,当从第3张图片继续向左滑动,会出现我们多添加的图片1,这时,将当前的index设置为真正的图片1所在的位置. package com.xiaomai

随机推荐