1. 程式人生 > 其它 >Unity-UGUI-無限迴圈列表

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 (int
i = 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,不會卡住的。由於我心煩氣亂,最終沒有做迴圈列表的方案。

但是迴圈列表的雛形是有的,以前都只知道迴圈列表的思路而未真正實現過,這次實踐是第一次,收穫頗豐,特寫隨筆記錄