Unity-UGUI-無限迴圈列表
前言:專案準備新增一個競技場排行榜,策劃規定只顯示0-400名的玩家。我一想,生成四百個遊戲物體,怕不是得把手機給卡死?回想原來在GitHub上看到過一個實現思路就是無限迴圈列表,所以就想自己試試。公司用的是NGUI,花了1個多小時還是沒做出來,但是基本思路有了,又到了下班時間,不想賴在公司怕帶壞了風氣,就回來用自己電腦上的UGUI實現。
實現思路:假設螢幕只顯示五行玩家排名,那麼就預備十行,上面多兩行,下面多三行。每次移動單位距離(一行玩家排名佔高),就移動一端的物體到另一端,這樣就實現了無限迴圈。
實現過程:
一、搭建UI
1.先在UI搭建好面板,其中SV物體新增RectMask2D元件,模擬ScrollView的裁剪;將無限迴圈列表指令碼掛在SV上
2.在SV下新增一個Grid(Grid並非元件而是名字)物體,用於模擬移動整個列表
3.Grid物體下新增十個Item,前面隱藏兩個,中間顯示五個,後面隱藏三個。為什麼要多放幾個呢?因為想滑動時更加順滑
二、實現LoopScroll類
1.類中包含欄位int[] keys,用於記錄下標變換;Dictionary<int,Transform> dic,用於儲存下標與Transform之間的對映關係;int gap,用於計算Transform之間應該間隔的距離
2.類中包含方法DownMove(),列表下移時呼叫;UpMove(),列表上移時呼叫
public class LoopScroll {private Dictionary<int, Transform> dic; private int[] keys; private float gap = 0; public LoopScroll(float gap,params Transform[] items) { this.gap = gap; dic = new Dictionary<int, Transform>(items.Length); keys = new int[items.Length]; for (inti = 0; i < items.Length; i++) { dic.Add(i, items[i]); keys[i] = i; } } private LoopScroll() { } public void DownMove() { //TODO 將頂部Trs移動到尾部 int top = keys[0]; for (int i = 0; i < keys.Length-1; i++) { keys[i] = keys[i + 1]; } keys[keys.Length - 1] = top; int key = keys[keys.Length - 2]; Vector3 pos = dic[top].localPosition; pos.Set(pos.x, dic[key].localPosition.y - gap, pos.z); dic[top].localPosition = pos; } public void UpMove() { //TODO 將尾部Trs移動到頂部 int down = keys[keys.Length - 1]; for (int i = keys.Length-1; i > 0; i--) { keys[i] = keys[i - 1]; } keys[0] = down; int key = keys[1]; Vector3 pos = dic[down].localPosition; pos.Set(pos.x, dic[key].localPosition.y+gap, pos.z); dic[down].localPosition = pos; } }
講解:每次移動時,對keys做迴圈類似的操作,操作完keys後,再通過下標獲取對應的Transform,將Transform插入到對應的Position。為什麼不直接用Transform[] 陣列處理呢?因為我擔心移動時會出現,因引用型別的特性,而導致位置混亂,還不如多用一個keys,keys是值型別,操作簡單。
三、具體實現
1.因為我是自己做的滑動,所以程式碼裡需要自己寫。當按下滑鼠左鍵,並且拖動一定距離時,觸發移動方法。在移動方法中判斷移動的偏移值的正負性,是向上?還是向下?然後move的position是加還是減?
2.move移動後,判斷move移動的距離是否大於等於單位距離(一個item的高),如果移動了單位距離,則觸發迴圈方法。移動的距離是正還是負?可得知是執行DownMove還是UpMove。
public class TestDragScroll : MonoBehaviour { public Transform startItem; public List<Transform> listTrs; public Transform move; private const float GAPY = 110f; private const float PerMoveDetal = 110f; private LoopScroll scorll; private float prePosY; private float movePrePosY; private void OnValidate() { Vector3 startPos = startItem.localPosition; float startY = startPos.y; for (int i = 0; i < listTrs.Count; i++) { listTrs[i].localPosition = new Vector3(startPos.x, startY, startPos.z); startY -= GAPY; } } // Start is called before the first frame update void Start() { scorll = new LoopScroll(GAPY, listTrs.ToArray()); prePosY = Input.mousePosition.y; movePrePosY = move.localPosition.y; } // Update is called once per frame void Update() { if (Input.GetMouseButton(0)) { float detal = Input.mousePosition.y - prePosY; if (Mathf.Abs(detal)>=1f) { prePosY = Input.mousePosition.y; Vector3 pos = move.localPosition; if (detal>0) { pos.Set(pos.x, pos.y + 4f, pos.z); } else { pos.Set(pos.x, pos.y - 4f, pos.z); } move.localPosition = pos; } } float moveDetal = move.localPosition.y - movePrePosY; if (Mathf.Abs(moveDetal)>=PerMoveDetal) { if (moveDetal>0) { scorll.DownMove(); Debug.Log("Down"); } else { scorll.UpMove(); Debug.Log("Up"); } movePrePosY = move.localPosition.y; } } }
四、專案實現效果
在自己電腦上用UGUI實現了並非適合專案場景。最終實現時,因為Update中判斷移動距離的方法並不可靠,移動距離每次不可能都=113,而我的判斷時只要>=113就執行一次迴圈,那麼我滑動226呢?還是隻移動了一個item。所以此方法行不通。我的主程告訴我,400個item並不一定要用無限迴圈列表,如果短時間內做不出來就不要再做了,專案進度要緊。建議我用協程分幀生成400個item,不會卡住的。由於我心煩氣亂,最終沒有做迴圈列表的方案。
但是迴圈列表的雛形是有的,以前都只知道迴圈列表的思路而未真正實現過,這次實踐是第一次,收穫頗豐,特寫隨筆記錄