Unity ScrollView實現(xiàn)無限循環(huán)效果
本文實例為大家分享了Unity ScrollView實現(xiàn)無限循環(huán)效果的具體代碼,供大家參考,具體內(nèi)容如下
在Unity引擎中ScrollView組件是一個使用率比較高的組件,該組件能上下或者左右拖動的UI列表,背包、展示多個按鈕等情況的時候會用到,在做排行榜類似界面時,item非常多,可能有幾百個,一次創(chuàng)建這么多GameObject是非??ǖ?。為此,使用只創(chuàng)建可視區(qū)一共顯示的個數(shù),加上后置準(zhǔn)備個數(shù)。
由于ScrollView有兩種滾動方式,水平滾動或者垂直滾動,所以我創(chuàng)建了ScrollBase基類,和HorizontalScroll子類及VerticalScroll子類,在父類中設(shè)定了公共的抽象方法OnDrawView—繪制顯示區(qū)的方法;CreateItem----創(chuàng)建Item的方法;Update----滾動狀態(tài)下實時更新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;//顯示區(qū)顯示的個數(shù) public int addNum;//后置準(zhǔn)備個數(shù) 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物體身上,然后在面板上進(jìn)行屬性配置
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("顯示區(qū)顯示幾個Item")] public int viewNum = 6; [Header("顯示區(qū)外多復(fù)制幾個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位置改變時調(diào)用的事件 [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
我們實現(xiàn)無限循環(huán)的核心思想是,當(dāng)滾動框向左移動時,就判斷左邊的第一個Item距離顯示框左邊框的距離,是否大于Item水平間隔*0.5+Item的寬度,如果大于則把第一個Item放到最后的位置,當(dāng)滾動框向右移動時就判斷右邊的Item距離右邊框的距離情況,這樣我們就可以在顯示區(qū)域一直看到有Item的規(guī)則顯示,所以我定制了一個泛型容器類,這個類存放所有的Item信息,當(dāng)我們?nèi)〉谝粋€Item時自動把第一個Item放到最后,當(dāng)我們?nèi)∽詈笠粋€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無限循環(huán)的核心功能代碼都在這兩個子類中
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; } }
編輯器拓展
這一步我們進(jìn)行編輯器的拓展,根據(jù)ScrollViewLoop控制類面板上的數(shù)據(jù)我們在未運行的狀態(tài)下改變ScrollView的顯示區(qū)域,這樣更利于我們的可視化排版。
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顯示區(qū)大小"); 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展示內(nèi)容 private void Sl_OnChange(ItemData itemData) { itemData.itemRect.GetChild(0).GetComponent<Text>().text = "ITEM"+itemData.index; } }
面板顯示如下:
運行效果如下(本來想放一段視頻的,遺憾的是沒能成功操作,尷尬):
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
利用MySqlBulkLoader實現(xiàn)批量插入數(shù)據(jù)的示例詳解
MySQLBulkLoader是MySQL?Connector/Net類中的一個類,用于包裝MySQL語句。本文將利用MySqlBulkLoader實現(xiàn)批量插入數(shù)據(jù)功能,感興趣的可以了解一下2022-06-06