Zhonghui

每个不曾起舞的日子,都是对生命的辜负

User Tools

Site Tools


软件:unity:滑动框

Unity 滑动框


滑动框的基础使用

基本结构就是: Scoll(Scroll Rect) > View(Mask) > Content > Items

无限循环列表

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
 
// 简单的无限循环列表,竖向滑动
// 第一个可以运行的版本,效率不高,拓展性不好,代码丑陋,还没有加上真实的数据
// 后续还可以加上的优化:使用Grid Layout组织Item,不用手动计算座标
// 无限循环列表的关键思路是,在Scroll Rect中把滑动的限制取消,可以无限滑动
// 实际的内容位置由自己手动维护
public class ISV : MonoBehaviour
{
    // Content对象,所有的Item都是Content的子对象
    private RectTransform m_Content = null;
 
    public GameObject Item; // 列表中每一项的模板
 
    public Vector2 CellSize; // 每一项的大小
 
    // 为了支持多列
    private int HorizontalItemCount; // 计算每一行有几个Item
    private int VerticalItemCount; // 计算有几列能覆盖满整个View
    private int AllItemCount; // 上面两个相乘:一共需要生成的Item的数量
 
    private bool m_Inited = false;
 
    // 这个应该是使用双端队列来做的,两个方向都有添加、删除的需求
    private List<GameObject> m_Items = new List<GameObject>(); // Item UI的列表
 
    // Start is called before the first frame update
    void Start()
    {
        m_Content = transform.Find("View/Content").GetComponent<RectTransform>();
 
        Vector2 ContentSize = m_Content.sizeDelta;
 
        // 计算Item数量
        HorizontalItemCount = (int)(ContentSize.x / CellSize.x);
        VerticalItemCount = (int)(ContentSize.y / CellSize.y) + 3;
        AllItemCount = HorizontalItemCount * VerticalItemCount;
 
        // 按照行列生成Item
        int nowIndex = 0;
        float nowY = CellSize.y;
        for (int i = 0; i < VerticalItemCount; i++)
        {
            float nowX = 0;
            for (int j = 0; j < HorizontalItemCount; j++)
            {
                // 生成Item
                var itemObj = GameObject.Instantiate(Item) as GameObject;
                itemObj.name = "item_" + (++nowIndex);
                itemObj.transform.SetParent(m_Content, false);
                itemObj.transform.localScale = Vector3.one;
                itemObj.GetComponent<RandomColor>().SetData(nowIndex); // 这个是Item需要实现的一个接口,很简单,为了做区分
                RectTransform Rect = itemObj.GetComponent<RectTransform>();
                if (Rect != null)
                {
                    // 注意使用的这个anchoredPosition
                    Rect.anchoredPosition = new Vector2(nowX, nowY); // 这里的座标都是自己维护的
                }
                m_Items.Add(itemObj);
                nowX += CellSize.x;
            }
            nowY -= CellSize.y;
        }
        Debug.Log("Init Done");
    }
 
    // Update is called once per frame
    void Update()
    {
        if(!m_Inited)
        {
            m_Inited = true;
        }
 
        for(int i=0;i<3;i+=1)
        {
            // 列表上面满了吗
            bool TopFull = Out(m_Items[0].GetComponent<RectTransform>());
            // 列表下面满了吗
            bool BottomFull = Out(m_Items[AllItemCount - 1].GetComponent<RectTransform>());
            if (!(TopFull && BottomFull))
            {
                // 有一方满了,另一方没有满的话,就把整行移过去
                if (TopFull)
                {
                    Move(true);
                }
                else if (BottomFull)
                {
                    Move(false);
                }
            }
            else break;
        }
    }
 
    private bool Out(RectTransform itemTrans)
    {
        float viewY = itemTrans.anchoredPosition.y + m_Content.anchoredPosition.y;
 
        // 判断满没满按照座标来
        return viewY > CellSize.y * 1.8 || viewY < (-m_Content.sizeDelta.y - CellSize.y * 0.8);
    }
 
    // 移动一整行,在双端队列中需要移动,Item UI的座标也需要重新计算
    private void Move(bool TopToBottom)
    {
        if (TopToBottom)
        {
            List<GameObject> Cache = new List<GameObject>();
            for (int i = 0; i < HorizontalItemCount; i += 1)
            {
                Cache.Add(m_Items[0]);
                m_Items.RemoveAt(0);
            }
            float nowX = 0, nowY = m_Items[m_Items.Count - 1].GetComponent<RectTransform>().anchoredPosition.y - CellSize.y;
            for (int i = 0; i < HorizontalItemCount; i += 1)
            {
                Cache[i].GetComponent<RectTransform>().anchoredPosition = new Vector2(nowX, nowY);
                m_Items.Add(Cache[i]);
                nowX += CellSize.x;
            }
        }
        else
        {
            List<GameObject> Cache = new List<GameObject>();
            for (int i = 0; i < HorizontalItemCount; i += 1)
            {
                Cache.Add(m_Items[AllItemCount - HorizontalItemCount]);
                m_Items.RemoveAt(AllItemCount - HorizontalItemCount);
            }
            float nowX = 0, nowY = m_Items[0].GetComponent<RectTransform>().anchoredPosition.y + CellSize.y;
            for (int i = 0; i < HorizontalItemCount; i += 1)
            {
                Cache[i].GetComponent<RectTransform>().anchoredPosition = new Vector2(nowX, nowY);
                m_Items.Insert(i, Cache[i]);
                nowX += CellSize.x;
            }
        }
    }
}
/var/www/DokuWikiStick/dokuwiki/data/pages/软件/unity/滑动框.txt · Last modified: 2022/08/24 06:44 by zh