游戏开发实战Unity循环复用列表,支持不规则尺寸(对象池 | UGUI | ScrollRect | Demo源码)

Posted 林新发

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了游戏开发实战Unity循环复用列表,支持不规则尺寸(对象池 | UGUI | ScrollRect | Demo源码)相关的知识,希望对你有一定的参考价值。

一、前言

嗨,大家好,我是新发。
有小伙伴在提问区问Unity如何实现不规则循环复用列表,

我今天发现GitHub上已经有人实现了一个版本,效果可以,
GitHub地址:https://github.com/aillieo/UnityDynamicScrollView
不过这个版本有个BUG,我做了修复,本文第三部分有说明。

好轮子大家用,今天我就介绍一下这个版本的使用方法吧~
本文最终效果如下

二、使用方法

1、创建Scroll View

UI节点上创建一个Scroll View

如下

Scroll View节点的Scroll Rect组件移除,如下

挂上代码中的ScrollView组件或ScrollViewEx组件,我这里以ScrollViewEx组件为例,

注:ScrollViewEx继承了ScrollView的所有功能,并进行了针对性的优化,它会对item进行分页,设置适当的页面尺寸可以得到更好的性能表现


挂上组件后效果如下,

2、设置Scroll View参数

2.1、调整宽高

我们先调整一下Scroll View的宽高,

2.2、删除Scrollbar滑块

我们看到Scroll View带了两个Scrollbar滑块,我们不想要它,

可以直接把子节点下的Scrollbar HorizontalScrollbar Vertical删除,

删除后可以看到Viewport之前给Scrollbar留了空间,现在没有Scrollbar了,我们要调节一下Viewport使其填充整个Scroll View

我们选中Viewport,把RightBottom都改为0

如下

2.3、设置item模板: Item Template

列表中要显示一个一个的item,得先做item模板,我们在Scroll View子节点下创建一个Image,重命名为item,如下,

调整一下item的宽高,

如下,

接着选中Scroll View节点,设置Item Template为刚刚的item,并填写Default Item Sizeitem的宽高,如下

2.4、设置对象池大小:Pool Size

为了防止列表item的重复创建销毁,这里用到了对象池,我们需要设置一下对象池大小Pool Size。如果一直往对象池塞对象(只塞不取),对象池满了之后,就不再继续塞对象到对象池中,我们需要设置合理的对象池大小,建议是列表可见区域能够显示的item的最大数量的2倍,这里我预估列表可见区域最多显示10item,那么我对象池大小设置为20

2.5、设置列表排列方向

列表提供了4种排列方向,大家一看选项就知道是什么意思了,这里我以Vertical为例,

2.6、设置分页大小: Page Size

如果你用的是ScrollView组件就没有Page Size这个设置了,只有ScrollViewEx组件有这个设置。
为什么要设置分页呢?ScrollView中维护了一份List<ScrollItemWithRect>,用于存储item的坐标和尺寸,

假设你的列表有巨量的item数据,你现在要往中间插入一个新的item,这个时候要重新计算巨量的item的坐标和尺寸,非常的耗性能,解决办法就是设置分页,每次只维护一个分页的item,大大提升性能。
建议设置为列表可见区域能够显示的item的最大数量的2倍以上,这里设置为30

2.7、其他常规设置

设置一下ContentViewport,如下

根据滑动方向勾选HorizontalVertical,这里我只需竖直方向滑动,所以只勾选Vertical

3、给item加点元素

上面我们的item光溜溜的,给它加点元素,再微调一下整体界面,如下

节点结构如下

4、写测试代码

创建一个MyTest.cs脚本,如下,

注:TestScript.csTestLargeAmount.cs是原作者提供的测试脚本。


先定义一个数据结构体

// MyTest.cs

public struct RankItemData

	// 名次
    public int rank;
    // 名字
    public string name;

声明一个List对象,用于存储列表数据,

// MyTest.cs

List<RankItemData> testData = new List<RankItemData>();

现在我们写个方法来构造一些测试数据,

// MyTest.cs

private void Start()

	// 构造测试数据
    InitData();


private void InitData()

	// 构建50000个排名数据
    for (int i = 1; i <= 50000; ++i)
    
        RankItemData data = new RankItemData();
        data.rank = i;
        data.name = "Name_" + i;
        testData.Add(data);
    

声明ScrollView成员对象,并给ScrollView设置回调函数,如下

// MyTest.cs

using System.Collections.Generic;
using UnityEngine;
using AillieoUtils;

public class MyTest : MonoBehaviour

	...
    public ScrollView scrollView;

	private void Start()
    
    	...
    	
        scrollView.SetUpdateFunc((index, rect) =>
        
            // TODO 更新item的UI元素
        );
        scrollView.SetItemSizeFunc((index) =>
        
            // TODO 返回item的尺寸
            return Vector2.one;
        );
        scrollView.SetItemCountFunc(() =>
        
            // TODO 返回数据列表item的总数
            return 0;
        );
    

接下来我们挨个实现回调的具体内容。
更新itemUI元素,

// MyTest.cs

scrollView.SetUpdateFunc((index, rectTransform) =>

    // 更新item的UI元素
    RankItemData data = testData[index];
    rectTransform.gameObject.SetActive(true);
    rectTransform.Find("rankText").GetComponent<Text>().text = data.rank.ToString();
    rectTransform.Find("nameText").GetComponent<Text>().text = data.name;
    Button btn = rectTransform.Find("Button").GetComponent<Button>();
    btn.onClick.RemoveAllListeners();
    btn.onClick.AddListener(()=>
        Debug.Log(data.name);
    );
);

返回item的尺寸,我希望前三名的item高度高一些,返回Vector2(812, 180),其他的返回Vector2(812, 100)

// MyTest.cs

scrollView.SetItemSizeFunc((index) =>

    // 返回item的尺寸
    RankItemData data = testData[index];
    if(data.rank <= 3)
    
        return new Vector2(812, 180);
    
    else
    
        return new Vector2(812, 100);
    
);

返回数据列表item的总数,

// MyTest.cs

scrollView.SetItemCountFunc(() =>

    // 返回数据列表item的总数
    return testData.Count;
);

5、运行测试

我们将MyTest.cs脚本挂到Canvas上,并赋值Scroll View成员,如下,

接着,我们把item隐藏掉,

运行,效果如下,

嗯,itemitem之间少了间隔,我们改造一下。

6、item直接增加间隔

item下创建一个Image并重命名为bg,设置Anchorsstretch-strech,设置TopBottom2,这样就会上下留2个单位的缝隙了,

我们把item自身的Image组件删掉,重新运行,效果如下,达到我们的效果了

我们可以看到,虽然我们的列表数据有50000个,但UI只有几个item在循环复用着,特别适合用于数据项很多的列表的显示,

三、BUG修复

测试的时候发现一个BUG,使用ScrollViewEx.cs时,翻页的时候,在边界时会疯狂触发OnValueChanged导致快速翻页,

我在Demo做了修复,增加了一个阻尼,避免疯狂翻页。

四、Demo源码

本文Demo源码我已上传到GitCode,感兴趣的同学可自行下载学习。
地址:https://codechina.csdn.net/linxinfa/UnityDynamicScrollView

五、完毕

好了,就写到这里吧。
我是新发,https://blog.csdn.net/linxinfa
一个在小公司默默奋斗的Unity开发者,希望可以帮助更多想学Unity的人,共勉~

以上是关于游戏开发实战Unity循环复用列表,支持不规则尺寸(对象池 | UGUI | ScrollRect | Demo源码)的主要内容,如果未能解决你的问题,请参考以下文章

游戏开发实战Unity循环复用列表,支持不规则尺寸(对象池 | UGUI | ScrollRect | Demo源码)

游戏开发实战Unity循环复用列表,支持不规则尺寸(对象池 | UGUI | ScrollRect | Demo源码)

Unity网络游戏实战读书笔记(一二章)Socket同步异步方法及多路复用

unity安卓开发尺寸

unity 如何设置plane的精确尺寸?

Unity游戏开发C#基础循环控制语句