博客迁移

个人博客站点,欢迎访问,www.jiingfengji.tech

正文

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

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

直接上脚本:

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,代码分别如下:

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);}}
}
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工程下载地址如下:
链接:http://pan.baidu.com/s/1i4Rralf 密码:7loe
链接如有私有,请及时联系补充

以上知识分享,如有错误,欢迎指出,共同学习,共同进步

Unity之图片轮播组件实现相关推荐

  1. vue 实现无限轮播_使用Vue制作图片轮播组件思路详解

    之前一直都没有认真的写过一个组件.以前在写业务代码的过程中,都是用的别人封装好的组件,这次尝试着写了一个图片轮播组件,虽然比不上知名的轮播组件,但它的功能基本完整,而且在写这个组件的过程中,学的东西也 ...

  2. unity实现图片轮播效果_Unity实现图片轮播组件

    游戏中有时候会见到图片轮播的效果,那么这里就自己封装了一个,包括自动轮播.切页按钮控制.页码下标更新.滑动轮播.切页后的回调等等 . 下面,先上一个简陋的gif动态效果图 从图中可以看出,该示例包括了 ...

  3. Angular2组件与指令的小实践——实现一个图片轮播组件

    如果说模块系统是Angular2的灵魂,那其组件体系就是其躯体,在模块的支持下渲染出所有用户直接看得见的东西,一个项目最表层的东西就是组件呈现的视图. 而除了直接看的见的躯体之外,一个完整的" ...

  4. bootstrap 轮播控制时间_【前端冷知识】如何封装一个图片轮播组件

    组件封装是一个前端工程师进阶的必经之路.组件封装是指Web页面上抽出来一个个包含模版(HTML).功能(Javascript)和样式(CSS)的单元.所以,今天的内容,我们将带你了解组件封装的开发思路 ...

  5. android 图片轮播组件,Android客户端实现图片轮播控件

    本文和大家一起写一个Android图片轮播控件,供大家参考,具体内容如下 1. 轮播控件的组成部分 我们以知乎日报Android客户端的轮播控件为例,分析一下轮播控件的主要组成: 首先我们要有用来显示 ...

  6. 造轮子之图片轮播组件(swiper)

    图片轮播是种很常见的场景和功能,一般移动网站首页的轮播 banner,商品详情页的商品图片等位置都会用到此功能 像这种常用的场景功能肯定是有人早就写好插件了的,所以遇到这种场景,一般都遵循以下三步: ...

  7. Omi-touch实战 移动端图片轮播组件的封装

    pc端的轮播,移动端的轮播都很常见.一年前,我还为手机端没有左滑,右滑事件从而封装了一个swipe库,可以自定义超过多少滑动时间就不触发,也可以设置滑动多少距离才触发,这一个功能的代码就达到400多行 ...

  8. Vue——图片轮播组件

    Notices: 这是我一个项目中的一个子组件,要展示的数据.图片地址等的都在父组件data中.所以后面的讲述都是基于从父组件获取的参数进行处理.(如需将这个SlideShow写成一个单独的主组件,将 ...

  9. unity实现图片轮播效果_unity 背景无限循环滚动效果

    背景无限循环滚动效果如下示: 步骤如下: 导入背景图片后,设置图片的格式,如下图: 2.图片格式也可以设置是Texture格式,但是Wrap Mode 一定要是Repeat[重复发生]:然后记得App ...

最新文章

  1. 人工智能抢80万工人的饭碗,真的会失业吗?但是有新的转机
  2. UA MATH567 高维统计IV Lipschitz组合8 随机投影与John-Lindenstrauss引理
  3. 采购订单相关Table
  4. 读《我们应当怎样做需求分析》后
  5. SilverLight 4页面跳转大全(转载)
  6. 【MFC系列3】永远点不到的按钮
  7. java 并发编程多线程_多线程(一)java并发编程基础知识
  8. 前端开发 标签的属性和值 0228 需演练
  9. IDEA用maven创建springMVC项目和配置(XML配置和Java配置)
  10. [py]__name__ 属于哪个文件
  11. CSS——淘宝-产品详情的制作
  12. 计算机word图表布布局在哪,word中的页面布局在哪里
  13. 二本计算机软件工程专业大学排名,哪些二本大学的软件工程专业最好
  14. 科学计算机js代码,利用js实现在线科学计算器代码
  15. 什么是 CI/CD?
  16. Excel如何快速隔行插入空行
  17. 利用DirectShow开发C#版的MP3播放器(二)
  18. educoder:实验七 元组和集合
  19. leetcode简单之577.员工奖金
  20. C语言实现CRC16和CRC32校验

热门文章

  1. Android开发 之 支付宝支付
  2. 如何将有打开密码的PDF转换成Word文件
  3. 线段求交算法对比研究
  4. redis分布式锁和看门狗的实现
  5. Redis笔记(狂神说)
  6. 淘宝昨日交易额达43.8亿 女装销量占4成
  7. MATLAB图像置乱混沌加密解密设计
  8. jsp页面中include的两种使用方法
  9. Android开发实战《手机安全卫士》——7.“高级工具”模块结尾 小火箭动画
  10. 微信分享给朋友接口图片问题