服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - Unity实现图片轮播组件

Unity实现图片轮播组件

2022-07-09 09:12qq_26999509 C#

这篇文章主要为大家详细介绍了Unity实现图片轮播组件的相关方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

游戏中有时候会见到图片轮播的效果,那么这里就自己封装了一个,包括自动轮播、切页按钮控制、页码下标更新、滑动轮播、切页后的回调等等 。

下面,先上一个简陋的gif动态效果图

Unity实现图片轮播组件

从图中可以看出,该示例包括了三张图片的轮播,左右分别是上一张和下一张的按钮,右下角显示了当前是第几章的页码下标。

直接上脚本:

?
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
 
namespace UnityEngine.UI
{
 [AddComponentMenu("UI/Slidershow", 39)] //添加菜单
 [ExecuteInEditMode]  //编辑模式下可执行
 [DisallowMultipleComponent]  //不可重复
 [RequireComponent(typeof(RectTransform))] //依赖于RectTransform组件
 public class Slideshow : UIBehaviour,IPointerDownHandler,IPointerUpHandler
 {
 public enum MovementType
 {
 /// <summary>
 /// 循环
 /// </summary>
 Circulation, //循环,轮播到最后一页之后,直接回到第一页
 
 /// <summary>
 /// 来回往复
 /// </summary>
 PingPong, //来回往复,轮播到最后一页之后,倒序轮播,到第一页之后,同理
 }
 
 public enum MoveDir
 {
 Left,
 Right,
 }
 
 [SerializeField]
 private MovementType m_movement = MovementType.Circulation;
 public MovementType Movement { get { return m_movement; } set { m_movement = value; } }
 
 [SerializeField]
 private RectTransform m_content;
 public RectTransform Content { get { return m_content; } set { m_content = value; } }
 
 [SerializeField]
 private Button m_lastPageButton;
 public Button LastPageButton { get { return m_lastPageButton; } set { m_lastPageButton = value; } }
 
 [SerializeField]
 private Button m_nextPageButton;
 public Button NextPageButton { get { return m_nextPageButton; } set { m_nextPageButton = value; } }
 
 /// <summary>
 /// 自动轮播时长
 /// </summary>
 [SerializeField]
 private float m_showTime = 2.0f;
 public float ShowTime { get { return m_showTime; } set { m_showTime = value; } }
 
 /// <summary>
 /// 是否自动轮播
 /// </summary>
 [SerializeField]
 private bool m_autoSlide = false;
 public bool AutoSlide { get { return m_autoSlide; }set { m_autoSlide = value; } }
 
 /// <summary>
 /// 自动轮播方向,-1表示向左,1表示向右
 /// </summary>
 private MoveDir m_autoSlideDir = MoveDir.Right;
 
 /// <summary>
 /// 是否允许拖动切页
 /// </summary>
 [SerializeField]
 private bool m_allowDrag = true;
 public bool AllowDrag { get { return m_allowDrag; }set { m_allowDrag = value; } }
 
 /// <summary>
 /// 当前显示页的页码,下标从0开始
 /// </summary>
 private int m_curPageIndex = 0;
 public int CurPageIndex { get { return m_curPageIndex; } }
 
 /// <summary>
 /// 最大页码
 /// </summary>
 private int m_maxPageIndex = 0;
 public int MaxPageIndex { get { return m_maxPageIndex; } }
 
 /// <summary>
 /// 圆圈页码ToggleGroup
 /// </summary>
 [SerializeField]
 private ToggleGroup m_pageToggleGroup;
 public ToggleGroup PageToggleGroup { get { return m_pageToggleGroup; } set { m_pageToggleGroup = value; } }
 
 /// <summary>
 /// 圆圈页码Toggle List
 /// </summary>
 private List<Toggle> m_pageToggleList;
 public List<Toggle> PageToggleLise { get { return m_pageToggleList; }}
 
 //item数目
 private int m_itemNum = 0;
 public int ItemNum { get { return m_itemNum; } }
 
 //以Toggle为Key,返回页码
 private Dictionary<Toggle, int> m_togglePageNumDic = null;
 
 private float m_time = 0f;
 
 private List<float> m_childItemPos = new List<float>();
 
 private GridLayoutGroup m_grid = null;
 
 protected override void Awake()
 {
 base.Awake();
 if (null == m_content)
 {
 throw new Exception("Slideshow content is null");
 }
 else
 {
 m_grid = m_content.GetComponent<GridLayoutGroup>();
 if (m_grid == null)
 {
  throw new Exception("Slideshow content is miss GridLayoutGroup Component");
 }
 InitChildItemPos();
 }
 
 
 if (null != m_lastPageButton)
 {
 m_lastPageButton.onClick.AddListener(OnLastPageButtonClick);
 }
 if (null != m_nextPageButton)
 {
 m_nextPageButton.onClick.AddListener(OnNextPageButtonClick);
 }
 if (null != m_pageToggleGroup)
 {
 int toggleNum = m_pageToggleGroup.transform.childCount;
 if (toggleNum > 0)
 {
  m_pageToggleList = new List<Toggle>();
  m_togglePageNumDic = new Dictionary<Toggle, int>();
  for (int i = 0; i < toggleNum; i++)
  {
  Toggle childToggle = m_pageToggleGroup.transform.GetChild(i).GetComponent<Toggle>();
  if (null != childToggle)
  {
  m_pageToggleList.Add(childToggle);
  m_togglePageNumDic.Add(childToggle, i);
  childToggle.onValueChanged.AddListener(OnPageToggleValueChanged);
  }
  }
  m_itemNum = m_pageToggleList.Count;
  m_maxPageIndex = m_pageToggleList.Count - 1;
 }
 }
 UpdateCutPageButtonActive(m_curPageIndex);
 }
 
 private void InitChildItemPos()
 {
 int childCount = m_content.transform.childCount;
 float cellSizeX = m_grid.cellSize.x;
 float spacingX = m_grid.spacing.x;
 float posX = -cellSizeX * 0.5f;
 m_childItemPos.Add(posX);
 for (int i = 1; i < childCount; i++)
 {
 posX -= cellSizeX + spacingX;
 m_childItemPos.Add(posX);
 }
 }
 
 private void OnPageToggleValueChanged(bool ison)
 {
 if (ison)
 {
 Toggle activeToggle = GetActivePageToggle();
 if (m_togglePageNumDic.ContainsKey(activeToggle))
 {
  int page = m_togglePageNumDic[activeToggle];
  SwitchToPageNum(page);
 }
 }
 }
 
 private Toggle GetActivePageToggle()
 {
 if (m_pageToggleGroup == null || m_pageToggleList == null || m_pageToggleList.Count <= 0)
 {
 return null;
 }
 for (int i = 0; i < m_pageToggleList.Count; i++)
 {
 if (m_pageToggleList[i].isOn)
 {
  return m_pageToggleList[i];
 }
 }
 return null;
 }
 
 /// <summary>
 /// 切换至某页
 /// </summary>
 /// <param name="pageNum">页码</param>
 private void SwitchToPageNum(int pageNum)
 {
 if (pageNum < 0 || pageNum > m_maxPageIndex)
 {
 throw new Exception("page num is error");
 }
 if (pageNum == m_curPageIndex)
 {
 //目标页与当前页是同一页
 return;
 }
 m_curPageIndex = pageNum;
 if (m_movement == MovementType.PingPong)
 {
 UpdateCutPageButtonActive(m_curPageIndex);
 }
 Vector3 pos = m_content.localPosition;
 m_content.localPosition = new Vector3(m_childItemPos[m_curPageIndex], pos.y, pos.z);
 m_pageToggleList[m_curPageIndex].isOn = true;
 
 if (m_onValueChanged != null)
 {
 //执行回调
 m_onValueChanged.Invoke(m_pageToggleList[m_curPageIndex].gameObject);
 }
 }
 
 /// <summary>
 /// 根据页码更新切页按钮active
 /// </summary>
 /// <param name="pageNum"></param>
 private void UpdateCutPageButtonActive(int pageNum)
 {
 if (pageNum == 0)
 {
 UpdateLastButtonActive(false);
 UpdateNextButtonActive(true);
 }
 else if (pageNum == m_maxPageIndex)
 {
 UpdateLastButtonActive(true);
 UpdateNextButtonActive(false);
 }
 else
 {
 UpdateLastButtonActive(true);
 UpdateNextButtonActive(true);
 }
 }
 
 private void OnNextPageButtonClick()
 {
 m_time = Time.time; //重新计时
 switch (m_movement)
 {
 case MovementType.Circulation:
  SwitchToPageNum((m_curPageIndex + 1) % m_itemNum);
  break;
 case MovementType.PingPong:
  //该模式下,会自动隐藏切页按钮
  SwitchToPageNum(m_curPageIndex + 1);
  break;
 default:
  break;
 }
 Debug.Log(m_content.localPosition);
 }
 
 private void OnLastPageButtonClick()
 {
 m_time = Time.time; //重新计时
 switch (m_movement)
 {
 case MovementType.Circulation:
  SwitchToPageNum((m_curPageIndex + m_itemNum - 1) % m_itemNum);
  break;
 case MovementType.PingPong:
  //该模式下,会自动隐藏切页按钮
  SwitchToPageNum(m_curPageIndex - 1);
  break;
 default:
  break;
 }
 }
 
 private void UpdateLastButtonActive(bool activeSelf)
 {
 if (null == m_lastPageButton)
 {
 throw new Exception("Last Page Button is null");
 }
 bool curActive = m_lastPageButton.gameObject.activeSelf;
 if (curActive != activeSelf)
 {
 m_lastPageButton.gameObject.SetActive(activeSelf);
 }
 }
 
 private void UpdateNextButtonActive(bool activeSelf)
 {
 if (null == m_nextPageButton)
 {
 throw new Exception("Next Page Button is null");
 }
 bool curActive = m_nextPageButton.gameObject.activeSelf;
 if (curActive != activeSelf)
 {
 m_nextPageButton.gameObject.SetActive(activeSelf);
 }
 }
 
 private Vector3 m_originDragPos = Vector3.zero;
 private Vector3 m_desDragPos = Vector3.zero;
 private bool m_isDrag = false;
 
 public void OnPointerDown(PointerEventData eventData)
 {
 if (!m_allowDrag)
 {
 return;
 }
 if (eventData.button != PointerEventData.InputButton.Left)
 {
 return;
 }
 if (!IsActive())
 {
 return;
 }
 
 m_isDrag = true;
 m_originDragPos = eventData.position;
 }
 
 public void OnPointerUp(PointerEventData eventData)
 {
 m_desDragPos = eventData.position;
 MoveDir dir = MoveDir.Right;
 if (m_desDragPos.x < m_originDragPos.x)
 {
 dir = MoveDir.Left;
 }
 switch (dir)
 {
 case MoveDir.Left:
  if (m_movement == MovementType.Circulation || (m_movement == MovementType.PingPong && m_curPageIndex != 0))
  {
  OnLastPageButtonClick();
  }
 
  break;
 case MoveDir.Right:
  if (m_movement == MovementType.Circulation || (m_movement == MovementType.PingPong && m_curPageIndex != m_maxPageIndex))
  {
  OnNextPageButtonClick();
  }
  break;
 }
 m_isDrag = false;
 }
 
 /// <summary>
 /// 切页后回调函数
 /// </summary>
 [Serializable]
 public class SlideshowEvent : UnityEvent<GameObject> { }
 
 [SerializeField]
 private SlideshowEvent m_onValueChanged = new SlideshowEvent();
 public SlideshowEvent OnValueChanged { get { return m_onValueChanged; } set { m_onValueChanged = value; } }
 
 public override bool IsActive()
 {
 return base.IsActive() && m_content != null;
 }
 
 private void Update()
 {
 if (m_autoSlide && !m_isDrag)
 {
 if (Time.time > m_time + m_showTime)
 {
  m_time = Time.time;
  switch (m_movement)
  {
  case MovementType.Circulation:
  m_autoSlideDir = MoveDir.Right;
  break;
  case MovementType.PingPong:
  if (m_curPageIndex == 0)
  {
  m_autoSlideDir = MoveDir.Right;
  }
  else if (m_curPageIndex == m_maxPageIndex)
  {
  m_autoSlideDir = MoveDir.Left;
  }
  break;
  }
  switch (m_autoSlideDir)
  {
  case MoveDir.Left:
  OnLastPageButtonClick();
  break;
  case MoveDir.Right:
  OnNextPageButtonClick();
  break;
  }
 }
 }
 }
 }
}

这里提供了一个枚举MovementType,该枚举定义了两种循环方式,其中Circulation循环,是指轮播到最后一页之后,直接回到第一页;而PingPong相信大家你熟悉了,就是来回往复的。

其中还提供了对每张图显示的时长进行设置,还有是否允许自动轮播的控制,是否允许拖动切页控制,等等。。其实将图片作为轮播子元素只是其中之一而已,完全可以将ScrollRect作为轮播子元素,这样每个子元素又可以滑动阅览了。

这里还提供了两个编辑器脚本,一个是SlideshowEditor(依赖Slideshow组件),另一个是给用户提供菜单用的CreateSlideshow,代码分别如下:

?
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
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
 
public class CreateSlideshow : Editor
{
 private static GameObject m_slideshowPrefab = null;
 
 private static GameObject m_canvas = null;
 
 [MenuItem("GameObject/UI/Slideshow")]
 static void CreateSlideshowUI(MenuCommand menuCommand)
 {
 if (null == m_slideshowPrefab)
 {
 m_slideshowPrefab = Resources.Load<GameObject>("Slideshow");
 if (null == m_slideshowPrefab)
 {
 Debug.LogError("Prefab Slideshow is null");
 return;
 }
 }
 
 m_canvas = menuCommand.context as GameObject;
 if (m_canvas == null || m_canvas.GetComponentInParent<Canvas>() == null)
 {
 m_canvas = GetOrCreateCanvasGameObject();
 }
 
 GameObject go = GameObject.Instantiate(m_slideshowPrefab, m_canvas.transform);
 go.transform.localPosition = Vector3.zero;
 go.name = "Slideshow";
 Selection.activeGameObject = go;
 }
 
 static public GameObject GetOrCreateCanvasGameObject()
 {
 GameObject selectedGo = Selection.activeGameObject;
 
 Canvas canvas = (selectedGo != null) ? selectedGo.GetComponentInParent<Canvas>() : null;
 if (canvas != null && canvas.gameObject.activeInHierarchy)
 return canvas.gameObject;
 
 canvas = Object.FindObjectOfType(typeof(Canvas)) as Canvas;
 if (canvas != null && canvas.gameObject.activeInHierarchy)
 return canvas.gameObject;
 
 return CreateCanvas();
 }
 
 public static GameObject CreateCanvas()
 {
 var root = new GameObject("Canvas");
 root.layer = LayerMask.NameToLayer("UI");
 Canvas canvas = root.AddComponent<Canvas>();
 canvas.renderMode = RenderMode.ScreenSpaceOverlay;
 root.AddComponent<CanvasScaler>();
 root.AddComponent<GraphicRaycaster>();
 Undo.RegisterCreatedObjectUndo(root, "Create " + root.name);
 
 CreateEventSystem();
 return root;
 }
 
 public static void CreateEventSystem()
 {
 var esys = Object.FindObjectOfType<EventSystem>();
 if (esys == null)
 {
 var eventSystem = new GameObject("EventSystem");
 GameObjectUtility.SetParentAndAlign(eventSystem, null);
 esys = eventSystem.AddComponent<EventSystem>();
 eventSystem.AddComponent<StandaloneInputModule>();
 
 Undo.RegisterCreatedObjectUndo(eventSystem, "Create " + eventSystem.name);
 }
 }
}
?
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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor.Advertisements;
using UnityEngine.UI;
 
namespace UnityEditor.UI
{
 [CustomEditor(typeof(Slideshow), true)]
 public class SlideshowEditor : Editor
 {
 SerializedProperty m_movement;
 SerializedProperty m_content;
 SerializedProperty m_lastPageButton;
 SerializedProperty m_nextPageButton;
 SerializedProperty m_showTime;
 SerializedProperty m_pageToggleGroup;
 SerializedProperty m_onValueChanged;
 SerializedProperty m_allowDrag;
 SerializedProperty m_autoSlide;
 
 protected virtual void OnEnable()
 {
 m_movement = serializedObject.FindProperty("m_movement");
 m_content = serializedObject.FindProperty("m_content");
 m_lastPageButton = serializedObject.FindProperty("m_lastPageButton");
 m_nextPageButton = serializedObject.FindProperty("m_nextPageButton");
 m_showTime = serializedObject.FindProperty("m_showTime");
 m_pageToggleGroup = serializedObject.FindProperty("m_pageToggleGroup");
 m_onValueChanged = serializedObject.FindProperty("m_onValueChanged");
 m_allowDrag = serializedObject.FindProperty("m_allowDrag");
 m_autoSlide = serializedObject.FindProperty("m_autoSlide");
 }
 
 public override void OnInspectorGUI()
 {
 serializedObject.Update();
 EditorGUILayout.PropertyField(m_movement);
 EditorGUILayout.PropertyField(m_content);
 EditorGUILayout.PropertyField(m_lastPageButton);
 EditorGUILayout.PropertyField(m_nextPageButton);
 EditorGUILayout.PropertyField(m_allowDrag);
 EditorGUILayout.PropertyField(m_autoSlide);
 EditorGUILayout.PropertyField(m_showTime);
 EditorGUILayout.PropertyField(m_pageToggleGroup);
 EditorGUILayout.Space();
 EditorGUILayout.PropertyField(m_onValueChanged);
 
 //不加这句代码,在编辑模式下,无法将物体拖拽赋值
 serializedObject.ApplyModifiedProperties();
 }
 }
}

这两个脚本中使用了一些拓展编辑器的知识,后续在另外写博客介绍 。

其中脚本CreateSlideshow中使用UGUI源码中的DefaultControls脚本里的方法,有兴趣可以去下载查阅。
Demo工程下载地址

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/qq_26999509/article/details/77435578

延伸 · 阅读

精彩推荐
  • C#C#中Json的简单处理方法

    C#中Json的简单处理方法

    这篇文章主要介绍了C#中Json的简单处理方法的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下...

    亲爱的小树9352021-12-06
  • C#深入解析C#中的泛型类与泛型接口

    深入解析C#中的泛型类与泛型接口

    这篇文章主要介绍了C#中的泛型类与泛型接口,对泛型的支持是C#语言的重要特性,需要的朋友可以参考下...

    C#教程网3822021-11-11
  • C#C#简单实现文件上传功能

    C#简单实现文件上传功能

    这篇文章主要介绍了C#简单实现文件上传功能,利用MVC+EF+LigerUI 实现的upload上传功能,感兴趣的小伙伴们可以参考一下...

    C#教程网10362021-11-16
  • C#C#验证码的创建与使用示例

    C#验证码的创建与使用示例

    这篇文章主要介绍了C#验证码的创建与使用方法,结合实例形式较为详细的分析了C#验证码的创建、验证等操作步骤与相关技巧,需要的朋友可以参考下...

    pan_junbiao4772021-12-22
  • C#Unity实现跑马灯抽奖效果

    Unity实现跑马灯抽奖效果

    这篇文章主要为大家详细介绍了Unity实现跑马灯抽奖效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Sweet_james7312022-03-10
  • C#C#实现语音视频录制-附demo源码

    C#实现语音视频录制-附demo源码

    在很多语言视频软件系统中,经常有将实时的音频或者是视频录制为文件保存到磁盘空间的需求,本篇给大家讲C#实现语音视频录制-附demo源码,感兴趣的朋...

    zhuweisky8762021-10-20
  • C#Winform开发框架中如何使用DevExpress的内置图标资源

    Winform开发框架中如何使用DevExpress的内置图标资源

    这篇文章主要给大家介绍了关于在Winform开发框架中如何使用DevExpress的内置图标资源的相关资料,文中通过图文介绍的非常详细,对大家的学习或者工作具...

    伍华聪4752022-03-06
  • C#UGUI绘制多点连续的平滑曲线

    UGUI绘制多点连续的平滑曲线

    这篇文章主要为大家详细介绍了UGUI绘制多点连续的平滑曲线,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    叫我上上4452022-03-07