我是靠谱客的博主 淡定老虎,这篇文章主要介绍Unity实现图片轮播组件,现在分享给大家,希望可以做个参考。

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

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

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

直接上脚本:

复制代码
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工程下载地址

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

最后

以上就是淡定老虎最近收集整理的关于Unity实现图片轮播组件的全部内容,更多相关Unity实现图片轮播组件内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部