如下图所示,这是我们的工程页面,程序的实现原理是将相册在Unity3D世界中呈横向队列,摄像机固定的照射在第一个Item相册,当手指发生滑动事件时,计算向左滑动还是向右滑动,此时整体移动相册队列,而摄像机不动。为了让滑动效果更加好看我们需要使用插值计算滑动的时间,使滑动队列不是直接移动过去,而是以一定惯性移动过去。相册下方我们制作一个小白点用来记录当前滑动的位置,在做几个灰色的点表示队列一共的长度,滑动时下方的小白点也会跟随移动,这样就更想高级控件啦。当然小白点与小灰点是要根据item的数量居中显示的喔。
注解1:滚动相册一般可分为两种,第一种为数量已知的情况,第二种为数量未知的情况。因为第一种比较简单所以我们主要探讨第二种。
Script historyInit.cs: 该脚本用于相册队列的初始化工作。在这里初始化相册队列的数量,计算完毕让队列以横向线性的排列方式在Unity3D中。
Prefab item:每个相册的预设,我这里每个相册上还会有一些文字的描述,我需要动态的修改它们的内容。大家也可根据自己的情况制作自己的相册item。
Prefabhui:相册滚动时下方用来记录相册队列总数的灰色小点。
Prefabbai :相册滚动时下方用来记录当前滚动页面ID的白色小点。
Point :因为灰色、白色的点不能和相册队列在一个面板之上,否则会跟着相册队列移动而移动,所以这里将灰色白色的点放在两外一个面板之上。
注解2:这个面板上的4个item就是我们通过historyinit脚本初始化时动态生成赋值的、当界面发生触摸事件时,会整体移动该面板让自对象的相册队列跟随移动。
注解3:button0 – button3 是下方的Tabar。bai(Clone)表示白色的小点,hui(Clone)表示灰色的小点,它们的位置是需要根据滑动事件而改变的。
因为我们需要监听每一个Item的滑动事件,所以肯定要在每一个item预设之上绑定监听事件的脚本,如下图所示。
注解1:因为需要监听触摸滑动事件,所以肯定要绑定Box Collider组件,这个是NGUI的标准用法。
注解2:Move脚本用来监听向左滑动 向右滑动 点击事件。
注解3:这个就是每一个相册的item,在上图中挂在historyInit脚本之上。
historyInit.cs
001 | using UnityEngine; |
002 | using System.Collections; |
003 | using System.Collections.Generic; |
004 |
005 | public class historyInit :MonoBehaviour |
006 | { |
007 |
008 |
//相册列表的每一个item |
009 |
public GameObject prefab; |
010 |
//灰色的小点 |
011 |
public GameObject prefabhui; |
012 |
//白色的小点 |
013 |
public GameObject prefabbai; |
014 |
//另外一个显示面板 |
015 |
//用来放置灰色、白色小点 |
016 |
public Transform ponit; |
017 |
//白色小点的临时对象 |
018 |
private GameObject bai; |
019 |
020 |
//链表,用来记录每一个相册中的一些用户信息 |
021 |
List<UserData> users = new List<UserData>(); |
022 |
//灰色、白色小点下方的起始位置。 |
023 |
int start; |
024 |
025 |
void Start () |
026 |
{ |
027 |
//将当前面板对象储存在全局静态变量中 |
028 |
Globe.ListPanel = gameObject; |
029 |
loadSQL (); |
030 |
initItem(); |
031 |
} |
032 |
033 |
//以前是读取数据库 |
034 |
//写例子程序就没必要使用数据库了 |
035 |
//这里直接写4个死值,当然数量是灵活使用的 |
036 |
037 |
void loadSQL () |
038 |
{ |
039 |
//表示一共向U3D世界中添加横向4个相册队列 |
040 |
for ( int i =0; i< 4; i ++) |
041 |
{ |
042 |
//简单的对象储存 |
043 |
string name = "momo =" + i; |
044 |
string age = "26 = " + i; |
045 |
string height = "183 =" + i; |
046 |
users.Add( new UserData(name,age,height)); |
047 |
} |
048 |
049 |
} |
050 |
051 |
void initItem() |
052 |
{ |
053 |
//因为下方灰色 白色的小点需要根据相册列表的数量来计算居中显示 |
054 |
int size = users.Count; |
055 |
//乘以16表示计算所有小点加起来的宽度 |
056 |
int length = (size - 1) * 16; |
057 |
//得到下方灰色 白色 小点的居中起始位置 |
058 |
start = (-length) >>1; |
059 |
060 |
for ( int i=0; i< size; i++) |
061 |
{ |
062 |
//把每一个相册加入相册列表 |
063 |
GameObject o =(GameObject) Instantiate(prefab); |
064 |
//设置这个对象的父类为 当前面板 |
065 |
o.transform.parent = transform; |
066 |
//设置相对父类的坐标,这些值可根据自己的情况而设定, |
067 |
//总之就是设置相册列表中每一个item的坐标,让它们横向的排列下来就行 |
068 |
o.transform.localPosition = new Vector3(25 + i* 243,-145f,-86f); |
069 |
//设置相对父类的缩放 |
070 |
o.transform.localScale= new Vector3(0.7349999f,0.66f,0.7349999f); |
071 |
072 |
//得到每一个user的信息 |
073 |
UserData data = users[i]; |
074 |
//遍历每一个相册对象的子组件, |
075 |
UILabel []label = o.GetComponentsInChildren<UILabel>(); |
076 |
//拿到UILabel并且设置它们的数据 |
077 |
label[0].text = data.age; |
078 |
label[1].text = data.height; |
079 |
label[2].text = data.name; |
080 |
081 |
//把每一个灰色小点加入3D世界 |
082 |
GameObject hui =(GameObject) Instantiate(prefabhui); |
083 |
//设置灰色小点的父类为另外一个面板 |
084 |
hui.transform.parent = ponit; |
085 |
//设置每一个灰色小点的位置与缩放,总之让它们居中排列显示在相册列表下方。 |
086 |
hui.transform.localPosition = new Vector3(start + i* 16,-120f,0f); |
087 |
hui.transform.localScale= new Vector3(8,8,1); |
088 |
089 |
//深度 因为是先在屏幕下方绘制4个灰色的小点, 然后在灰色上面绘制白色小点 |
090 |
//表示当前的窗口ID 所以深度是为了设置白色小点在灰色小点之上绘制 |
091 |
hui.GetComponent<UISprite>().depth = 0; |
092 |
} |
093 |
094 |
//下面的数据是把当前初始化的数据放在一个static类中 |
095 |
//在Move脚本中就可以根据这里的数据来判断了。 |
096 |
//滑动列表的长度 |
097 |
Globe.list_count = size -1; |
098 |
//相册每一项的宽度 |
099 |
Globe.list_offset = 243; |
100 |
//当前滑动的索引 |
101 |
Globe.list_currentIndex = 0; |
102 |
//点击后打开的新游戏场景 |
103 |
Globe.list_go_name= "LoadScene" ; |
104 |
105 |
//把白色小点也加载在3D世界中 |
106 |
bai =(GameObject) Instantiate(prefabbai); |
107 |
//设置它的深度高于 灰色小点,让白色小点显示在灰色小点之上 |
108 |
bai.GetComponent<UISprite>().depth = 1; |
109 |
//设置白色小点的位置 |
110 |
setBaiPos(); |
111 |
} |
112 |
113 |
void Update() |
114 |
{ |
115 |
//当用户滑动界面 |
116 |
//在Update方法中更新 |
117 |
//当前白色小点的位置 |
118 |
setBaiPos(); |
119 |
} |
120 |
121 |
void setBaiPos() |
122 |
{ |
123 |
//Globe.list_currentIndex 就是当前界面的ID |
124 |
//根据ID 重新计算白色小点的位置 |
125 |
bai.transform.parent = ponit; |
126 |
bai.transform.localPosition = new Vector3(start + Globe.list_currentIndex* 16,-120f,-10f); |
127 |
bai.transform.localScale= new Vector3(8,8,1); |
128 |
129 |
} |
130 | } |
如下图所示,我们可以看出Tabbar 、 下方记录界面的灰色、白色小点、摄像机 它们是不会发生改变的。唯一改变的就是面板之上的相册队列。为了让滑动界面的效果更加连贯,我们需要以插值的形式来计算真个相册面板的坐标。
触摸的事件全都写在Move.cs脚本中。
01 | using UnityEngine; |
02 | using System.Collections; |
03 |
04 | public class Move : MonoBehaviour { |
05 |
06 |
//是否触摸 |
07 |
bool isTouch = false ; |
08 |
//是否向左滑动 |
09 |
bool isRight = false ; |
10 |
//是否向右滑动 |
11 |
bool isLeft = false ; |
12 |
//是否正在滑动中 |
13 |
bool isOnDrag = false ; |
14 |
15 |
//滑动中事件 |
16 |
void OnDrag (Vector2 delta) |
17 |
{ |
18 |
//为了避免事件冲突 |
19 |
//这里只判断一个滑动的事件 |
20 |
if (!isTouch) |
21 |
{ |
22 |
if (delta.x > 0.5) |
23 |
{ |
24 |
//向左滑动 |
25 |
isRight = true ; |
26 |
isOnDrag = true ; |
27 |
} else if (delta.x < -0.5) |
28 |
{ |
29 |
//向右滑动 |
30 |
isLeft = true ; |
31 |
isOnDrag = true ; |
32 |
} |
33 |
isTouch = true ; |
34 |
} |
35 |
} |
36 |
37 |
//滑动后松手调用OnPress事件 |
38 |
void OnPress() |
39 |
{ |
40 |
//重新计算当前界面的ID |
41 |
if (Globe.list_currentIndex < Globe.list_count && isLeft) |
42 |
{ |
43 |
Globe.list_currentIndex++; |
44 |
} |
45 |
46 |
if (Globe.list_currentIndex >0 && isRight) |
47 |
{ |
48 |
Globe.list_currentIndex--; |
49 |
} |
50 |
51 |
//表示一次滑动事件结束 |
52 |
isTouch = false ; |
53 |
isLeft = false ; |
54 |
isRight = false ; |
55 |
} |
56 |
57 |
void Update() |
58 |
{ |
59 |
//这个方法就是本节内容的核心 |
60 |
//Globe.ListPanel 这个对象是我们在historyInit脚本中得到的,它用来当面相册面板对象使用插值,让面板有惯性的滑动。 |
61 |
62 |
//Vector3.Lerp() 这个是一个插值的方法, 参数1 表示开始的位置 参数2 表示结束的位置 参数3 表示一共所用的时间, 在Time.deltaTime * 5 时间以内 Update每一帧中得到插值当前的数值,只到插值结束 |
63 |
64 |
//-(Globe.list_currentIndex * Globe.list_offset) 得到当前需要滑动的目标点。 |
65 |
//请大家仔细看这个方法。 |
66 |
67 |
Globe.ListPanel.transform.localPosition =Vector3.Lerp(Globe.ListPanel.transform.localPosition, new Vector3(-(Globe.list_currentIndex * Globe.list_offset),0,0), Time.deltaTime * 5); |
68 |
} |
69 |
70 |
void OnClick () |
71 |
{ |
72 |
//当点击某个item时进入这里 |
73 |
74 |
if (!isOnDrag) |
75 |
{ |
76 |
//如果不是在拖动中 进入另一个场景 |
77 |
Application.LoadLevel(Globe.list_go_name); |
78 |
} |
79 |
else |
80 |
{ |
81 |
//否则等待用户重新发生触摸事件 |
82 |
isOnDrag = false ; |
83 |
} |
84 |
} |
85 | } |
还有两个辅助的类,我也贴出来。
UserData.cs
01 | using UnityEngine; |
02 | using System.Collections; |
03 |
04 | public class UserData{ |
05 |
06 |
public string name; |
07 |
public string age; |
08 |
public string height; |
09 |
public string hand; |
10 |
11 |
public UserData( string _name, string _age, string _height) |
12 |
{ |
13 |
age = _age; |
14 |
height = _height; |
15 |
name = _name; |
16 |
} |
17 |
18 | } |
Globe.cs 这个静态类用来共享记录多个脚本甚至多个场景所需的数据。
01 | using System.Collections.Generic; |
02 | public class Globe |
03 | { |
04 |
05 |
public static GameObject ListPanel; |
06 |
public static int list_count; |
07 |
public static int list_currentIndex; |
08 |
public static int list_offset; |
09 |
public static string list_go_name; |
10 |
11 | } |
写到这里,本篇博文就写的差不多了。这篇文章我是用NGUI来实现的触摸滚动效果,仔细想想,其实不用NGUI完全也能实现这样的效果。在脚本中完全可以通过射线 以及 触摸的时间去计算当前用户操作的事件,这篇文章里的工程最后我是打包在Android上面的,效果挺不错,滑动的效果图不好截取我也不截取了,主要还是文章的书写内容,希望大家学习愉快,雨松MOMO祝大家晚安,哇咔咔,啦啦啦。