这篇文章主要为大家详细介绍了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;
}
}
面板显示如下:
运行效果如下(本来想放一段视频的,遗憾的是没能成功操作,尴尬):
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持得得之家。
本文标题为:Unity ScrollView实现无限循环效果
基础教程推荐
- winform把Office转成PDF文件 2023-06-14
- C#类和结构详解 2023-05-30
- unity实现动态排行榜 2023-04-27
- linux – 如何在Debian Jessie中安装dotnet core sdk 2023-09-26
- C# List实现行转列的通用方案 2022-11-02
- ZooKeeper的安装及部署教程 2023-01-22
- C# windows语音识别与朗读实例 2023-04-27
- 一个读写csv文件的C#类 2022-11-06
- C# 调用WebService的方法 2023-03-09
- C#控制台实现飞行棋小游戏 2023-04-22