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

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

下面,先上一个简陋的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

{

///

/// 循环

///

Circulation, //循环,轮播到最后一页之后,直接回到第一页

///

/// 来回往复

///

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; } }

///

/// 自动轮播时长

///

[SerializeField]

private float m_showTime = 2.0f;

public float ShowTime { get { return m_showTime; } set { m_showTime = value; } }

///

/// 是否自动轮播

///

[SerializeField]

private bool m_autoSlide = false;

public bool AutoSlide { get { return m_autoSlide; }set { m_autoSlide = value; } }

///

/// 自动轮播方向,-1表示向左,1表示向右

///

private MoveDir m_autoSlideDir = MoveDir.Right;

///

/// 是否允许拖动切页

///

[SerializeField]

private bool m_allowDrag = true;

public bool AllowDrag { get { return m_allowDrag; }set { m_allowDrag = value; } }

///

/// 当前显示页的页码,下标从0开始

///

private int m_curPageIndex = 0;

public int CurPageIndex { get { return m_curPageIndex; } }

///

/// 最大页码

///

private int m_maxPageIndex = 0;

public int MaxPageIndex { get { return m_maxPageIndex; } }

///

/// 圆圈页码ToggleGroup

///

[SerializeField]

private ToggleGroup m_pageToggleGroup;

public ToggleGroup PageToggleGroup { get { return m_pageToggleGroup; } set { m_pageToggleGroup = value; } }

///

/// 圆圈页码Toggle List

///

private List m_pageToggleList;

public List PageToggleLise { get { return m_pageToggleList; }}

//item数目

private int m_itemNum = 0;

public int ItemNum { get { return m_itemNum; } }

//以Toggle为Key,返回页码

private Dictionary m_togglePageNumDic = null;

private float m_time = 0f;

private List m_childItemPos = new List();

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();

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();

m_togglePageNumDic = new Dictionary();

for (int i = 0; i < toggleNum; i++)

{

Toggle childToggle = m_pageToggleGroup.transform.GetChild(i).GetComponent();

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;

}

///

/// 切换至某页

///

/// 页码

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);

}

}

///

/// 根据页码更新切页按钮active

///

///

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;

}

///

/// 切页后回调函数

///

[Serializable]

public class SlideshowEvent : UnityEvent { }

[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("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() == 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() : 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.renderMode = RenderMode.ScreenSpaceOverlay;

root.AddComponent();

root.AddComponent();

Undo.RegisterCreatedObjectUndo(root, "Create " + root.name);

CreateEventSystem();

return root;

}

public static void CreateEventSystem()

{

var esys = Object.FindObjectOfType();

if (esys == null)

{

var eventSystem = new GameObject("EventSystem");

GameObjectUtility.SetParentAndAlign(eventSystem, null);

esys = eventSystem.AddComponent();

eventSystem.AddComponent();

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://www.mzph.cn/news/259914.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[置顶] 2013腾讯编程马拉松初赛第4场(3月24)(HDU 4520 HDU4521 HDU4522 HDU4523 HDU4524)...

话说昨天比赛终于拿到一个不错的名次&#xff0c;rank77&#xff0c;对于我们这种ACM弱菜的学校来说已经很好了&#xff0c;可惜我1003用了俩floyd超时&#xff0c;如果我最近稍微搞搞图论的话&#xff0c;用个bellman&#xff0c;或者SPFA&#xff0c;绝对超不了了就。。。哎。…

计算机学院年会,重庆大学计算机学院举行2019年迎新晚会

2019年12月6号晚&#xff0c;重庆大学计算机学院2019年迎新晚会在兰园小剧场举行。出席本次晚会的嘉宾有计算机学院党委副书记兼纪委书记郭坤银、党委组织员刘霜、2016级辅导员李若菡老师、2017级辅导员古曦老师、2018级辅导员郑田青老师、2019级辅导员谢璧如老师。本次晚会的主…

[转贴]Cocos2d-x3.2与OpenGL渲染总结(一)Cocos2d-x3.2的渲染流程

看了opengles有一段时间了&#xff0c;算是了解了一下下。然后&#xff0c;就在基本要决定还是回归cocos2dx 3.2的&#xff0c;看了这篇好文章&#xff0c;欣喜转之~ 推荐看原帖&#xff1a; Cocos2d-x3.2与OpenGL渲染总结(一)Cocos2d-x3.2的渲染流程 最近几天&#xff0c;我都…

省赛热身赛之Median

原题&#xff1a; Description A median is described as the numeric value separating the higher half of a list, from the lower half. The median of a finite list of numbers can be found by arranging all the elements from lowest value to highest value and pick…

win32 段寄存器怎么寻址

32位cpu 地址线扩展成了32位&#xff0c;这和数据线的宽度是一致的。因此&#xff0c;在32位机里其实并不需要采用“物理地址段&#xff1a;偏移”这种地址表达方式。原来在16位机里规定的 每一个段不大于64kb在32位机里也不是必要的。所以&#xff0c;对于32位机来讲&#xff…

联想拯救者y7000p加内存条_笔记本怎么升级内存和硬盘 联想Y7000P加装内存和硬盘图文教程 (全文)...

一般目前新买的笔记本电脑,大都是标配8GB内存和单块固态硬盘,内存和硬盘容量适中,但对于一些制图设计、偏大型游戏,又或者对硬盘存储要求比较高的用户来说,显然就不太够用,这时候我们一般会通过升级内存和硬盘来解决。那么,笔记本怎么升级内存和硬盘?下面以联想Y7000P笔…

计算机组装与维护实训1,计算机组装与维护实训报告[1]

计算机组装与维护实训报告[1] (12页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;11.90 积分实习报告设计题目&#xff1a; 计算机组装与维护实习 专业班级&#xff1a; 计算机应用103班 学生姓名&a…

node.js-------使用路由模块

路由需要的信息&#xff0c;包括URL 及GET 或 POST参数。路由根据这些参数执行相应的js处理程序&#xff0c;因此&#xff0c;需要在HTTP请求中提取出URL以及GET或POST参数。这些请求参数在request对象中&#xff0c;这个对象是onRequest()回调函数的第一个参数。需要提取这些信…

Docker - 在CentOS 7中安装Docker

在CentOS 7中安装Docker 1-确认系统信息 # cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) # uname -a Linux CentOS-7 3.10.0-327.el7.x86_64 #1 SMP Thu Nov 19 22:10:57 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux 2-安装docker # yum -y install docker 3…

HDU 1715 大菲波数 (大数问题)

/* 复习大数问题&#xff1b; */ #include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <string> #include <iomanip> using namespace std;int nu…

springcloud 相同服务名_SpringCloud系列之SpringCloud Stream

SpringCloud Stream技术兴起的原因&#xff1a;为了解决系统中不同中间件的适配问题&#xff0c;出现了cloud stream&#xff0c;采用适配绑定的方式&#xff0c;自动给不同的MQ之间进行切换。屏蔽底层消息中间件的差异&#xff0c;降低切换成本&#xff0c;统一消息的编程模型…

计算机意外重启或遇错误无法继续,计算机意外地重新启动或遇到错误如何解决?...

电脑小白在重装系统后难免会遇到些问题&#xff0c;有的容易处理&#xff0c;有的会有些棘手。那么&#xff0c;计算机意外地重新启动或遇到错误如何解决?今天快启动小编为大家分享详细的计算机意外地重新启动或遇到错误的解决方法&#xff0c;献给对系统重装知识不太了解的小…

jqueryui的Tooltip使用方法

http://api.jqueryui.com/tooltip/#option-position&#xff0c;详细使用方法。 http://jqueryui.com/tooltip/&#xff0c;DEMO。 content使用 $( ".selector" ).tooltip({ content: "Awesome title!" });//div及相关标签使用样式&#xff0c;鼠标放上去时…

iOS 开发者账号共用发布证书 (Distribution)问题

苹果客服回复&#xff1a; 1.第一台申请发布证书的电脑&#xff0c;从钥匙串中导出发布证书(Distribution)颁发的request文件&#xff1f;然后在第二台电脑上用request文件新生成一个Distribution证书&#xff0c;这个是可以共用的&#xff1f;&#xff08;不理解还是理解错了&…

JMeter web 测试

2019独角兽企业重金招聘Python工程师标准>>> JMeter web 测试 http://jmeter.apache.org/usermanual/build-web-test-plan.html 转载于:https://my.oschina.net/276172622/blog/808957

scala 连接oracle_一分钟教你学会用java连接Oracle数据库

package java_jdbc;//java连接Oracle数据库import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.Statement;public class JdbcOracleTest {public static void main(String[] args) {// TODO Auto-generated method stub//1.…

计算机软件记不住设置,想知道电脑密码记不住了怎么办

635509517回答数&#xff1a;23216 | 被采纳数&#xff1a;32017-01-09 17:51:10方法一&#xff1a;(1)启动电脑&#xff0c;使用DOS启动盘(比如&#xff1a;Windows 98启动盘)进入纯DOS状态。(2)在DOS提示符下&#xff0c;根据下面步骤操作&#xff1a;cd\\ (切换到根目录)c…

vue-cli#2.0 webpack 配置分析

目录结构&#xff1a; ├── README.md ├── build │ ├── build.js │ ├── check-versions.js │ ├── dev-client.js │ ├── dev-server.js │ ├── utils.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js…

initWithNibName与viewDidLoad的执行关系以及顺序

一个ViewController&#xff0c;一般通过init或initWithNibName来加载。二者没有什么不同&#xff0c;init最终还是要调用initWithNibName方法&#xff08;除非这个ViewController没有nib文件&#xff09;。 我们经常在initWithNibName方法中初始化视图&#xff0c;变量或者其他…

120xa正反转参数_你知道变频器的“正反转死区时间”吗?它的“停机方式”有几种?...

若你我之间有缘&#xff0c;关注作者又何妨&#xff1f;两情若是久长时&#xff0c;又岂在朝朝暮暮。大家好&#xff01;我是江郎&#xff0c;一个踏踏实实的维修工。本期我们仍然探讨两个问题&#xff0c;如标题所述&#xff0c;#变频器#“死区时间”和“停机方式”&#xff0…