我是靠谱客的博主 义气冬日,这篇文章主要介绍Unity ScrollView实现无限循环效果,现在分享给大家,希望可以做个参考。

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

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

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

ScrollBase基类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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物体身上,然后在面板上进行属性配置

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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放到最前面。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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子类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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子类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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的显示区域,这样更利于我们的可视化排版。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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); } } } }

测试

测试一下功能效果

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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实现无限循环效果的全部内容,更多相关Unity内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(149)

评论列表共有 0 条评论

立即
投稿
返回
顶部