(一)unity优化方案:优化NGUI中ScrollView海量数据的显示(适用于游戏中排行榜,商城等可滚动界面的显示)

游戏中的排行榜,商城等可滚动显示的界面通常要显示海量的数据,如果直接把所有数据对应的item实例化到Grid列表下面,作为一名游戏开发者,这么做是极为不明智的,通常用的技巧就是重用item。打个比方:如果有千条数据需要显示在界面中,但界面只能一次显示3条数据信息,那么重用item的思想只需要最多最开始实例化出4个item加入带Grid列表下面即可,而不是实例化出千个item,无疑大大提高了游戏性能。

接下来我说一下重用item的基本思想:当滚动列表向上移动时(也就是鼠标或触屏向上移动,数据向下显示),就把超出屏幕上部的item移动到列表末尾位置并重置数据。当滚动列表向下移动时(也就是鼠标或触屏向下移动,数据向上显示),就把超出屏幕下部的item移动到列表开头。思想说完了,接下来开始正式开发Demo了。

一、制作一个scrollview滚动界面

这里我是比着NGUI的官方Demo(下载NGUI插件并导入untiy,然后会有一个Example7-Scroll View(Panel).unity)制作的。如下图所示,官方Demo是一个可以横向滚动的滚动列表,而我要做成一个竖向滚动的列表(一般排行榜、商城都是竖向显示数据的哈哈哈,再来自己动手做一遍效果比较好)。

(一)unity优化方案:优化NGUI中ScrollView海量数据的显示(适用于游戏中排行榜,商城等可滚动界面的显示)_第1张图片

制作完scrollview后,在grid下创建一个我们想要的Item,不要忘记添加BosCollider和UIDragScrollView,BosCollider要勾选IsTrigger。并制作成预制体。制作完后运行场景,可以看到一个item,并且可以在ScrollView范围内拖拽.。

下面是我做的效果图:

(一)unity优化方案:优化NGUI中ScrollView海量数据的显示(适用于游戏中排行榜,商城等可滚动界面的显示)_第2张图片

二、编写脚本

2.1 编写数据类脚本ItemData,代码如下:

/// 
/// item数据类
/// 
public class ItemData {

    /// 
    /// item的索引
    /// 
    public int index;
    //item的价格
    public string money;
    public ItemData()
    {

    }
    public ItemData(int index,string money)
    {
        this.index = index;
        this.money = money;
    }
}

这个类记录了列表中每个item的初始数据,成员字段有两个,一个是索引index,一个是物品显示的价格money(item的价格,为了方便展示效果)。

 

2.2编写脚本Item,代码如下:

/// 
/// 该类挂载再item预设上,用于显示每条item的数据
/// 
public class Item : MonoBehaviour {
    [SerializeField]
    private UILabel label;
    public ItemData data;
	// Use this for initialization
	void Start () {
        //label = transform.Find("MoneyLabel").GetComponent();
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}
    /// 
    /// 显示该item的价钱
    /// 
    /// 
    public void setMoney(ItemData itemData)
    {
        this.data = itemData;
        label.text = data.money;
    }
}

这里声明了一个ItemData类的引用,记录当前的item显示哪条数据,label显示该数据的价格数据。这个脚本要放在item预设体上。

 

2.3编写核心脚本ScrollViewManager,代码如下:

public class ScrollViewManager : MonoBehaviour {

    //保存所有的item物体以及列表中数据
    List itemDataList = new List();
    List itemList = new List();

    UIScrollView sv;
    UIGrid grid;
    //用于判断scrollView的移动方向,记录下位置
    float svLastPos = 0;
    //最大和最小y坐标,判断item移出屏幕的最大和最小距离
    float maxHeight;
    float minHeight;

	// Use this for initialization
	void Start () {
        sv = transform.GetComponent();
        grid = transform.Find("Grid").GetComponent();
        sv.transform.localPosition = new Vector3(0,0,0);
        //100个数据初始化
        for(int i = 0; i < 100; i++)
        {
            itemDataList.Add(new ItemData(i, string.Format("${0}", i+1)));
        }

        //计算要生成多少个item对象,用整个ScrollView的高度除以每个item的高度得到屏幕中一共能显示多少个item,然后加上1个以避免item不足的情况出现。
        //面板的大小——可以是剪切矩形,也可以是屏幕尺寸。
        Vector2 viewsize = transform.GetComponent().GetViewSize();
        //设置item数量,数量为屏幕最多可显示item数量+1
       int count = (int)(viewsize.y / grid.cellHeight + 1);
        Debug.Log(count);
        //for循环:实例化出item+1数量的item实体,初始化数据,并设置数据后放在Grid下边
        for(int i = 0; i < count; i++)
        {
            //终止条件:如果itemData总数小于i,不在实例化item
            if (itemDataList.Count <= i)
               break;
            GameObject go = Resources.Load("Prefab/Item") as GameObject;
            GameObject obj = NGUITools.AddChild(grid.gameObject, go);
            Item itemAdd = obj.GetComponent();
            itemAdd.setMoney(itemDataList[i]);
            itemList.Add(itemAdd);

        }
        //在下一次更新中重新定位子节点。
        grid.repositionNow = true;
        //重新计算网格中所有元素的位置,按字母顺序排序。
        grid.Reposition();
        svLastPos = grid.transform.localPosition.y;
        //计算屏幕的最大高度和最小高度(这里的计算随count+1或+2有所改变)
        //maxHeight = maxHeight = viewsize.y / 2 + grid.cellHeight / 2;
        maxHeight = grid.cellHeight;
        minHeight = -maxHeight;
        Debug.Log(sv.transform.localPosition.y);

	}
	
	// Update is called once per frame
	void Update () {
        //判断列表是否移动,用现在的位置减之前记录的位置,如果移动了,重置item
        float moveDis = sv.transform.localPosition.y - svLastPos;
        Debug.Log(sv.transform.localPosition.y);
        if (Mathf.Abs(moveDis) > 0.05f)
        {
            bool isup = moveDis > 0;
            //如果面板向上滚动
            if (isup)
            {
                //判断item列表中第0个元素是否超出坐标的最大范围
                //还要判断数据列表中是否超出范围,如果超出范围说明没有数据了,不需要重置item位置了。
                //itemList[0].transform.localPosition.y必须加上,不然就会直接拖到底了。这个参数在while循环最后y轴每次减去一个item的高度值,所以一直呈负数增长
                while (itemList[0].transform.localPosition.y + sv.transform.localPosition.y > maxHeight &&
                    itemList[itemList.Count-1].data.index < itemDataList.Count-1)
                {
                    //移动item,首先将item中的数据设置为目标数据,这里直接取item列表最后一个元素获取到数据的index并且+1就是目标数据
                    Item item = itemList[0];
                    item.setMoney(itemDataList[itemList[itemList.Count-1].data.index + 1]);
                    //将当前item添加到列表末尾同时删除列表第一个元素,最后设置item的坐标。
                    itemList.Add(item);
                    itemList.RemoveAt(0);
                    //这要注意!!!!!!是-2
                    item.transform.localPosition = itemList[itemList.Count-2].transform.localPosition -
                         new Vector3(0, grid.cellHeight, 0);

                }
            }
            //如果面板向下滚动
            else
            {
                //判断item列表中最后一个元素是否低于坐标的最小范围
                //还要判断数据列表中是否超出范围,如果超出范围说明没有数据了,不需要重置item位置了。
                while (itemList[itemList.Count-1].transform.localPosition.y + sv.transform.localPosition.y < minHeight &&
                    itemList[0].data.index >0)
                {
                    // //移动item,首先将item中的数据设置为目标数据,这里直接取item列表第一个元素获取到数据的index并且-1就是目标数据
                    Item item = itemList[itemList.Count-1];
                    item.setMoney(itemDataList[itemList[0].data.index-1]);
                    //将当前item插入到列表开头同时删除列表最后一个元素,最后设置item的坐标。
                    itemList.Insert(0, item);
                    itemList.RemoveAt(itemList.Count-1);

                    item.transform.localPosition = itemList[1].transform.localPosition +
                        new Vector3(0, grid.cellHeight, 0);
                }

            }
        }

        svLastPos = sv.transform.localPosition.y;
    }
}

虽然我在代码中写了大量详细的注释,但是我还是要写一下具体过程,这块是最核心的代码。

1.首先定义了两个集合List,分别存储item物体(要实例化到Grid列表下的,如果界面一次最多显示3个item,那么就最多实例化出4个即可)和列表中所有要显示的数据(有100条就存100条)。

2.定义了变量svLastPos用来记录最后一次scrollview的y轴相对位置坐标(上下拖动会发生变化),这是为了后面判断移动方向考虑的,UIScrollView的API中并没有判断移动方向的相关接口。maxHeight和minHeight用来判断移出屏幕的最大值和最小值距离。

3.Start方法中:

(1)首先初始化数据,假设有100条数据要显示。并把数据对象赋值后添加到集合中。

(2)计算要生成多少个item对象,用整个ScrollView的高度除以每个Item的高度得到界面中最多能显示几个item,然后加上1个作为显示用的预设,这样做才不会拖动显示时显得太突兀或不符合常理。

(3)for循环:实例化出item+1个实体,初始化数据,添加到itemList集合中,并作为Grid列表的子对象。注意终止条件,如果界面可显示数据量为3,但是实际只有两条数据,那么实例化完成后就直接跳出循环了。

(4)计算屏幕大小的最大和最小高度。高度为面板界面高度的一半加上单个item的一半。

4.Update方法中:

(1)首先判断列表是否移动了,用scrollview现在的位置减去之前记录的最后的scrollview位置,如果移动了,并且移动距离的绝对值大于0.05,就进行重置item的逻辑。

(2)接下来判断移动方向,如果面板向上滚动,判断item列表中第0个元素是否超出坐标的最大范围。还要判断数据列表中是否超出范围,如果超出范围说明没有数据了,不需要重置item位置了。itemList[0].transform.localPosition.y必须加上,不然就会直接拖到底了。这个参数在while循环最后y轴每次减去一个item的高度值,所以一直呈负数增长。如果要移动item,首先将item中的数据设置为目标数据,这里直接取item列表最后一个元素获取到数据的index并且+1就是目标数据,然后将当前item添加到列表末尾同时删除列表第一个元素,最后设置item的坐标,这里要注意,是itemList[itemList.Count-2]的局部位置,不是itemList[itemList.Count-1,因为已经把该item添加到集合末尾了。向上移动和向下移动的逻辑正好相反,这里不再复述。

至此,优化海量数据的方案已经完成,希望能够给一些爱好者提供一点思路和帮助。

你可能感兴趣的:(unity优化方案)