NGUI中提供了两种Scroll View 一种是经过手指或鼠标滑动视图时移动平面物体,另外一种则是直接移动摄像机,他们各有各的好处。可是NGUI提供的Scroll View很难实现相似Android 与 IOS 中的Scroll View 滚动相册的那种效果,不过程序猿的力量是伟大无穷的。虽然不能用它提供的API作出来,可是咱们能够经过另外的手打巧妙的实现。这篇文章仔细向你们介绍如何实现自制Scroll View实现滚动相册。php
以下图所示数据库
这是咱们的工程页面,程序的实现原理是将相册在Unity3D世界中呈横向队列,摄像机固定的照射在第一个Item相册,当手指发生滑动事件时,计算向左滑动仍是向右滑动,此时总体移动相册队列,而摄像机不动。为了让滑动效果更加好看咱们须要使用插值计算滑动的时间,使滑动队列不是直接移动过去,而是以必定惯性移动过去。相册下方咱们制做一个小白点用来记录当前滑动的位置,在作几个灰色的点表示队列一共的长度,滑动时下方的小白点也会跟随移动,这样就更想高级控件啦。固然小白点与小灰点是要根据item的数量居中显示的喔。这个面板上的4个CardItem就是咱们经过historyinit脚本初始化时动态生成赋值的、当界面发生触摸事件时,会总体移动该面板让自对象的相册队列跟随移动。White(Clone)表示白色的小点,gray(Clone)表示灰色的小点,它们的位置是须要根据滑动事件而改变的。ide
首先咱们来看下,Panel的的脚本historyInit.csui
using UnityEngine; using System.Collections; using System.Collections.Generic; public class historyInit : MonoBehaviour { //相册列表的每个item public GameObject prefab; //灰色的小点 public GameObject prefabhui; //白色的小点 public GameObject prefabbai; //另一个显示面板 //用来放置灰色、白色小点 public Transform ponit; //白色小点的临时对象 private GameObject bai; //链表,用来记录每个相册中的一些用户信息 List<UserData> users = new List<UserData>(); //灰色、白色小点下方的起始位置。 int start; void Start() { //将当前面板对象储存在全局静态变量中 Globe.ListPanel = gameObject; loadSQL(); initItem(); } //之前是读取数据库 //写例子程序就不必使用数据库了 //这里直接写4个死值,固然数量是灵活使用的 void loadSQL() { //表示一共向U3D世界中添加横向4个相册队列 for (int i = 0; i < 4; i++) { //简单的对象储存 string name = "momo =" + i; string age = "26 = " + i; string height = "183 =" + i; users.Add(new UserData(name, age, height)); } } void initItem() { //由于下方灰色 白色的小点须要根据相册列表的数量来计算居中显示 int size = users.Count; //乘以16表示计算全部小点加起来的宽度 int length = (size - 1) * 16; //获得下方灰色 白色 小点的居中起始位置 start = (-length) >> 1; for (int i = 0; i < size; i++) { //把每个相册加入相册列表 GameObject o = (GameObject)Instantiate(prefab); //设置这个对象的父类为 当前面板 o.transform.parent = transform; //设置相对父类的坐标,这些值可根据本身的状况而设定, //总之就是设置相册列表中每个item的坐标,让它们横向的排列下来就行 o.transform.localPosition = new Vector3(25 + i * 243, -145f, -86f); //设置相对父类的缩放 o.transform.localScale = new Vector3(0.7349999f, 0.66f, 0.7349999f); //获得每个user的信息 UserData data = users[i]; Move moveScript = o.GetComponent<Move>(); moveScript.AgeLabel.text = data.age; moveScript.HeightLabel.text = data.height; moveScript.NameLabel.text = data.name; ////遍历每个相册对象的子组件, //UILabel[] label = o.GetComponentsInChildren<UILabel>(); ////拿到UILabel而且设置它们的数据 //label[1].text = data.age; //label[2].text = data.height; //label[3].text = data.name; //把每个灰色小点加入3D世界 GameObject hui = (GameObject)Instantiate(prefabhui); //设置灰色小点的父类为另一个面板 hui.transform.parent = ponit; //设置每个灰色小点的位置与缩放,总之让它们居中排列显示在相册列表下方。 hui.transform.localPosition = new Vector3(start + i * 16, -120f, 0f); hui.transform.localScale = new Vector3(8, 8, 1); //深度 由于是先在屏幕下方绘制4个灰色的小点, 而后在灰色上面绘制白色小点 //表示当前的窗口ID 因此深度是为了设置白色小点在灰色小点之上绘制 hui.GetComponent<UISprite>().depth = 0; } //下面的数据是把当前初始化的数据放在一个static类中 //在Move脚本中就能够根据这里的数据来判断了。 //滑动列表的长度 Globe.list_count = size - 1; //相册每一项的宽度 Globe.list_offset = 243; //当前滑动的索引 Globe.list_currentIndex = 0; //点击后打开的新游戏场景 Globe.list_go_name = "LoadScene"; //把白色小点也加载在3D世界中 bai = (GameObject)Instantiate(prefabbai); print(bai); //设置它的深度高于 灰色小点,让白色小点显示在灰色小点之上 bai.GetComponent<UISprite>().depth = 1; //设置白色小点的位置 setBaiPos(); } void Update() { //当用户滑动界面 //在Update方法中更新 //当前白色小点的位置 setBaiPos(); } void setBaiPos() { //Globe.list_currentIndex 就是当前界面的ID //根据ID 从新计算白色小点的位置 bai.transform.parent = ponit; bai.transform.localPosition = new Vector3(start + Globe.list_currentIndex * 16, -120f, -10f); bai.transform.localScale = new Vector3(8, 8, 1); } }
该脚本用于相册队列的初始化工做。在这里初始化相册队列的数量,计算完毕让队列以横向线性的排列方式在Unity3D中。spa
Prefab:每一个相册的预设,我这里每一个相册上还会有一些文字的描述,我须要动态的修改它们的内容。你们也可根据本身的状况制做本身的相册item。3d
Prefabhui:相册滚动时下方用来记录相册队列总数的灰色小点。code
Prefabbai :相册滚动时下方用来记录当前滚动页面ID的白色小点。orm
Point :由于灰色、白色的点不能和相册队列在一个面板之上,不然会跟着相册队列移动而移动,因此这里将灰色白色的点放在两外一个面板之上。对象
咱们须要监听每个CardItem的滑动事件,因此确定要在每个CardItem预设之上绑定监听事件的脚本,这里是Move.cs,以下图所示。blog
由于须要监听触摸滑动事件,因此确定要绑定Box Collider组件,这个是NGUI的标准用法。
Move脚本用来监听向左滑动 向右滑动 点击事件。
触摸的事件全都写在Move.cs脚本中。
using UnityEngine; using System.Collections; public class Move : MonoBehaviour { //是否触摸 bool isTouch = false; //是否向左滑动 bool isRight = false; //是否向右滑动 bool isLeft = false; //是否正在滑动中 bool isOnDrag = false; public UILabel AgeLabel; public UILabel HeightLabel; public UILabel NameLabel; public UILabel UserLabel; //滑动中事件 void OnDrag(Vector2 delta) { //为了不事件冲突 //这里只判断一个滑动的事件 if (!isTouch) { if (delta.x > 0.5) { //向左滑动 isRight = true; isOnDrag = true; } else if (delta.x < -0.5) { //向右滑动 isLeft = true; isOnDrag = true; } isTouch = true; } } //滑动后松手调用OnPress事件 void OnPress() { //从新计算当前界面的ID if (Globe.list_currentIndex < Globe.list_count && isLeft) { Globe.list_currentIndex++; } if (Globe.list_currentIndex > 0 && isRight) { Globe.list_currentIndex--; } //表示一次滑动事件结束 isTouch = false; isLeft = false; isRight = false; } void Update() { //这个方法就是本节内容的核心 //Globe.ListPanel 这个对象是咱们在historyInit脚本中获得的,它用来当面相册面板对象使用插值,让面板有惯性的滑动。 //Vector3.Lerp() 这个是一个插值的方法, 参数1 表示开始的位置 参数2 表示结束的位置 参数3 表示一共所用的时间, 在Time.deltaTime * 5 时间之内 Update每一帧中获得插值当前的数值,只到插值结束 //-(Globe.list_currentIndex * Globe.list_offset) 获得当前须要滑动的目标点。 //请你们仔细看这个方法。 Globe.ListPanel.transform.localPosition = Vector3.Lerp(Globe.ListPanel.transform.localPosition, new Vector3(-(Globe.list_currentIndex * Globe.list_offset), 0, 0), Time.deltaTime * 5); } void OnClick() { //当点击某个item时进入这里 if (!isOnDrag) { //若是不是在拖动中 进入另外一个场景 //Application.LoadLevel(Globe.list_go_name); } else { //不然等待用户从新发生触摸事件 isOnDrag = false; } } }
还有两个辅助的类,我也贴出来。
UserData.cs
using UnityEngine; using System.Collections; public class UserData { public string name; public string age; public string height; public string hand; public UserData(string _name, string _age, string _height) { age = _age; height = _height; name = _name; } }
Globe.cs 这个静态类用来共享记录多个脚本甚至多个场景所需的数据。
using System.Collections.Generic; using UnityEngine; public class Globe { public static GameObject ListPanel; public static int list_count; public static int list_currentIndex; public static int list_offset; public static string list_go_name; }
参考:http://www.xuanyusong.com/archives/1465
个人项目:http://pan.ceeger.com/viewfile.php?file_id=1827&file_key=KOdeQf5o