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