VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > C#教程 >
  • Unity ScrollView实现无限循环效果

这篇文章主要为大家详细介绍了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;

    }

}

面板显示如下:

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

以上就是本文的全部内容

原文链接:https://blog.csdn.net/weixin_42498461/article/details/106836320


相关教程