如下图所示,这是我们的工程页面,程序的实现原理是将相册在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祝大家晚安,哇咔咔,啦啦啦。