Unity|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 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(); scrollRect = scrollLoop.GetComponent(); if (!scrollRect){scrollRect = scrollLoop.gameObject.AddComponent(); }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
Unity|Unity ScrollView实现无限循环效果
文章图片

我们实现无限循环的核心思想是,当滚动框向左移动时,就判断左边的第一个Item距离显示框左边框的距离,是否大于Item水平间隔*0.5+Item的宽度,如果大于则把第一个Item放到最后的位置,当滚动框向右移动时就判断右边的Item距离右边框的距离情况,这样我们就可以在显示区域一直看到有Item的规则显示,所以我定制了一个泛型容器类,这个类存放所有的Item信息,当我们取第一个Item时自动把第一个Item放到最后,当我们取最后一个Item时自动把最后的一个Item放到最前面。
using System.Collections; using System.Collections.Generic; using UnityEngine; /// /// Item集合类/// /// public class ItemArray where T : ItemData{List list; public int count{get{return list.Count; }}public ItemArray(){list = new List(); }public ItemArray(T[] ts){if (ts == null){return; }for (int i = 0; i < ts.Length; i++){list.Add(ts[i]); }}/// /// 添加一个元素/// /// 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(); for (int i = 0; i < viewNum + addNum; i++){GameObject item = GameObject.Instantiate(scrollLoop.itemPrefab); RectTransform itemRect = item.GetComponent(); ItemData data = https://www.it610.com/article/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(); for (int i = 0; i < viewNum + addNum; i++){GameObject item = GameObject.Instantiate(scrollLoop.itemPrefab); RectTransform itemRect = item.GetComponent(); ItemData data = https://www.it610.com/article/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(); scrollRect = scrollLoop.GetComponent(); }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 = "ITEM"+itemData.index; }}

面板显示如下:
Unity|Unity ScrollView实现无限循环效果
文章图片

运行效果如下(本来想放一段视频的,遗憾的是没能成功操作,尴尬):
Unity|Unity ScrollView实现无限循环效果
文章图片

【Unity|Unity ScrollView实现无限循环效果】以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    推荐阅读