【Unity】优化UGUI 滚动条ScrollRect(高效复用)

article/2025/10/11 13:56:27

最近忙于性能优化,深切体会到二八法则真是指导高(tou)效(lan)工作的有力武器。这个礼拜花了几天解决了一个实际问题:UGUI的ScrollRect加载太多物体的时候,第一次弹出界面会非常卡顿,而且不在界面里的内容依然会参与绘制(毫无意义的浪费…)。

demo1

ChangeLog

  • v1.03 终于支持了ScrollBar,支持直接创建
  • v1.02 Bug Fixes,无尽模式
  • v1.01 重构了好几遍,基本算重写了份…优化了拖动手感和回收部分的计算,增加了反方向滑动支持。升级至Unity 5.2的UGUI API
  • v1.0 这两天基于网上找的一份InfinityScroll代码,把这个功能做了。在加载时间和Draw Call上都提升显著,而且滑动的时候也没有卡顿

每个元素知道自己的序号,可以根据需要修改自己的内容、大小等信息。

此外支持了ScrollBar,支持横向、纵向及正反向。

demo1

在关闭Mask后可以看到,只有当需要的时候才动态实例化元素,使用完后回收。

demo3

介绍

最原始版本的代码是@ivomarel的InfinityScroll。我改到后来,基本和原始版没啥相同的了…

  • 原代码使用了sizeDelta作为大小,但是这个在锚点不重合情况下是不成立的
  • 支持了GridLayout
  • 在启动时检查锚点和轴心,方便使用
  • 修复了原代码在往前拖拽会卡顿的问题
  • 优化代码,提升性能
  • 支持反向滑动
  • 支持ScrollBar (在无尽模式下不起作用; 如果元素大小不一致会出现滚动条瑕疵)

此外,我修改了Easy Object Pool作为池子,循环利用元素。

警告: 为了解决原始代码回拉卡顿的问题,我直接复制了一份UGUI中的ScrollRect代码,而没有继承。这是因为老的做法是在onDrag里停止并立即启动滚动,而我通过修改两个私有变量保证了滑动顺畅。所有我的代码都用==========LoopScrollRect==========这样的注释包起来,维护起来就像打patch了…

框架思路

和UGUI自带的ScrollRect有所不同,我拆分出了LoopHorizontalScrollRectLoopVerticalScrollRect两个类,分别代表水平滚动条和水平滚动条。下面我们以LoopVerticalScrollRect为例,水平版本类似。

判定cell大小

LoopScrollRect要解决的核心问题是:如何计算每个元素的大小。这里我使用了Content Size Fitter配合Layout Element来控制每个cell的长宽,因此对于GridLayout直接取高度,否则取Preferred Height。需要注意的是,除了元素本身的大小之外,我们还要将padding考虑进去。


这个其实也是最核心的一个地方:在能够准确计算格子大小的基础上,后续工作就好实现了。

如何优雅的增删元素

对于每个ScrollRect,其实只需要考虑在头部和尾部是否需要增加或者删除元素。在这里以头部的各种情况为例进行解释,因为在正向滑动情况下,必须保证在修改元素之后整个ScrollRect内容显示一致不跳变;这些情况比尾部处理会麻烦一些。

NewItemAtStart函数实现了在头部增加一个(或一行,针对GridLayout)元素,并返回这些元素的高度;DeleteItemAtStart代表删除头部的一个元素。需要注意的是,在修改头部元素之后要及时修改contentanchoredPosition,这样才能保证整个内容区域不会因为多了或者少了一行而产生跳变。


何时需要增删元素

这里需要有两个概念viewBoundscontentBounds:前者是指ScrollRect本身的大小,一般也对应Mask;后者是指ScrollRect里所有cell组成的内容部分的大小。在这个基础上就简单了:如果contentBounds的最上面比viewBounds的最上面要低,那么尝试在顶部增加元素;如果contentBounds的最上面比viewBounds的最上面高很多,那么尝试删除元素。


对象池交互

在新建cell和销毁cell的时候,使用对象池来避免内存碎片;同时这里使用了SendMessage来向每个cell发送必须的信息,保证数据的正确性。


滚动条相关

这块我其实是估算的,根据当前的长度和当前元素个数/总个数按照比例缩放,这个在所有cell大小一致的情况下是没有问题的;但是如果大小不一致我就无法得到精确结果,所以会产生一定抖动。我暂时没有更好办法,因为得到的信息就是不够用…

其他细节

我主要遇到了两个坑:

  • 增加或者删除元素之后,有时候需要强行调用Canvas.ForceUpdateCanvases()刷新下
  • 注意不要在Build Canvas过程中再次修改元素,从而再次触发Build Canvas…

使用示例

以竖直滚动条为例,介绍一下步骤。如果觉得麻烦的话,直接打开DemoScene复制粘贴就好 =w= 当然你也可以干掉EasyObjPool,自己控制生成和销毁。

  • 准备好Prefabs
    • 每个物体上需要贴上Layout Element并指定preferred width/height
    • 贴上一个脚本接受void ScrollCellIndex (int idx) 消息,从而对每个位置的元素根据需要灵活修改

ScrollCell

  • 在Hierarchy里右键,选择UI/Loop Horizontal Scroll RectUI/Loop Vertical Scroll Rect即可。使用Component菜单里的也是一样的。
    • Init in Start: 启动时自动调用Refill cells初始化
    • Prefab Pool: EasyObjPool物体
    • Prefab Pool Name: 第二步中对应的Cell Prefab名字
    • Total Count: 总共能有多少物体,范围0 ~ TotalCount-1
    • Threshold: 两端预留出来的缓存量(像素数)
    • ReverseDirection: 如果是从下往上或者从右往左拖动,就打开这里
    • Clear Cells: 清除已有元素,恢复到未初始化状态
    • Refill Cells: 初始化并填充元素

LoopVerticalScrollRect

如果是正向滑动,就设置pivot为1;否则设为0并打开ReverseDirection。我强烈建议你试试在播放状态下试试看修改这些参数。

无尽模式

如果需要无限滚动模式,将totalCount设为负数即可。

他人工作

后来搜了下,发现网上也有人提到过UGUI ScrollRect 优化,不过他的策略是监听ScrollRect的value,然后禁用范围外的cell。最后作者也提到改成动态加载策略。这种基于value的做法我不太确认在在滚动前动态添加新元素的时候是否会出现问题。


http://chatgpt.dhexx.cn/article/g3bZ90Vn.shtml

相关文章

vue 切换页面没有改变滚动条_Vue真是太好了 壹万多字的Vue知识点 超详细!

1⃣️、Vue和其他两大框架的区别 Angular 学习成本太高React 代码可读性差Vue 学习成本较低 很容易上手VUE官方: https://cn.vuejs.org/v2/guide/comparison.html ️2⃣️、Vue是什么 Vue是一套用于构建用户界面的渐进式框架 "前端框架"让程序员脱离自己操作DOM 专注…

前端低代码平台腾讯云微搭使用文档

腾讯云微搭 调研报告 之前作者有写过一个同类低代码平台调研报告 H5-Dooring 点击查看,这次我们去尝试使用腾讯系低代码平台,文中也会增加两者之间的差异对比和使用体验上的区别。 1. 简介 1.1 概述 腾讯云微搭低代码是一个高性能的低代码开发平台&a…

Android Patch方案与持续交付

Android 不仅系统版本众多,机型众多,而且各个市场都各有各的政策和审核速度,每次发布一个版本对于开发同学来讲都是一种漫长的煎熬。相比于 iOS 两三天就能达到 80% 的覆盖速度而言,Android 应用版本升级至少需要两周才能达到 80%…

element-ui el-table组件添加height属性后滚动条被顶下去一截

el-table 组件添加了height属性,数据行数超标,出现滚动条; 同时给table中的一列添加了 fixed“right” 这个属性,然后又在项目里自定义了滚动条样式,这个滚动条跟固定列会被挤下去,造成错位的bug。建议去掉…

已解决:element Table 滚动条首次进入不显示、偶尔切换页面后不显示,刷新当前页或改变窗口才显示

记录一下在项目中遇到的问题,困扰了几天最终解决了。 一、问题:element Table 滚动条首次进入不显示、偶尔切换页面后不显示,刷新当前页或改变窗口才显示。 1、首次进入的效果 可以看到滚动条并没有渲染出来,但是刷新页面或者改…

Android Patch 方案与持续交付

Android 不仅系统版本众多,机型众多,而且各个市场都各有各的政策和审核速度,每次发布一个版本对于开发同学来讲都是一种漫长的煎熬。相比于 iOS 两三天就能达到 80% 的覆盖速度而言,Android 应用版本升级至少需要两周才能达到 80%…

js表格冻结列滚动条同步滚动

用div css写的table表格怎么实现冻结某列&#xff0c;同时实现数据滚动条滚动的时候&#xff0c;表头也跟着滚动呢&#xff1f; 效果图&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html> <head><meta charset"utf-8"><style>.f…

损失函数分类

损失函数 机器学习模型关于单个样本的预测值与真实值的差称为损失。损失越小&#xff0c;模型越好&#xff0c;如果预测值与真实值相等&#xff0c;就是没有损失。 损失函数&#xff08;Loss function&#xff09;是用来估量模型的预测值 f(x) 与真实值 Y 的不一致程度&#x…

损失函数总结

1. 概况 损失函数一般分为&#xff1a;0-1 损失函数&#xff0c;HingeLoss&#xff0c;绝对值损失函数&#xff0c;Huber Loss, 平方损失函数&#xff0c;对数损失函数&#xff0c;指数损失。 1. 0-1损失函数(zero-one loss) 0-1损失是指预测值和目标值不相等为1&#xff0…

matlab的损失函数mse,MSELoss损失函数

MSELoss损失函数中文名字就是&#xff1a;均方损失函数&#xff0c;公式如下所示&#xff1a; 这里 loss, x, y 的维度是一样的&#xff0c;可以是向量或者矩阵&#xff0c;i 是下标。 很多的 loss 函数都有 size_average 和 reduce 两个布尔类型的参数。因为一般损失函数都是直…

损失函数Loss Fuction

说说代价函数的作用&#xff1f; 1.为得到训练模型的参数&#xff0c;需要一个代价函数&#xff0c;通过训练代价函数来得到参数。    2.用于找到最优解的目的函数。 说说代价函数为什么要非负&#xff1f; 因为目标函数存在下界&#xff0c;在优化过程当中&#xff0c;如果优…

常用的损失函数

来自 机器学习成长之路公众号 本文将常用的损失函数分为了两大类&#xff1a;分类和回归。然后又分别对这两类进行了细分和讲解&#xff0c;其中回归中包含了一种不太常见的损失函数&#xff1a;平均偏差误差&#xff0c;可以用来确定模型中存在正偏差还是负偏差。 从学习任务…

损失函数设计

目录 1.常见损失函数 1.1 平方损失函数 1.2 绝对值损失函数 1.3 Huber损失函数 1.4 Hinge损失函数 1.5 交叉熵损失函数 1.6 指数损失函数 2.不对称损失函数设计 3.面向容错的损失函数设计 4.评测指标不可导时的损失函数设计 5.没有“Groud Truth“的损失函数设计 6…

如何选择合适的损失函数

【AI科技大本营导读】机器学习中的所有算法都依赖于最小化或最大化某一个函数&#xff0c;我们称之为“目标函数”。最小化的这组函数被称为“损失函数”。损失函数是衡量预测模型预测期望结果表现的指标。寻找函数最小值的最常用方法是“梯度下降”。把损失函数想象成起伏的山…

常用的损失函数合集

目录 一、什么是损失函数&#xff1f; 二、为什么需要损失函数&#xff1f; 三、损失函数通常使用的位置 四、损失函数的分类 五、常用的损失函数 1、回归损失&#xff08;针对连续型变量&#xff09; 1.L1 Loss也称为Mean Absolute Error&#xff0c;即平均绝对误差&…

语义分割的损失函数

语义分割任务实际上是一种像素层面上的分类&#xff0c;需要识别图像中存在的内容和位置&#xff0c;同样也存在与分类类似问题-样本类别不平衡&#xff0c;对于语义分割更多的是前景区域的样本远小于背景区域。针对类别不平衡问题&#xff0c;在loss层面上有不同的选择。 1. …

损失函数简介

损失函数的作用&#xff1a;衡量模型预测的好坏。 简单来说就是&#xff0c;损失函数就是用来表现预测与真实数据的差距程度。 令 真实值 为Y&#xff0c;预测值为 f(x)&#xff0c;损失函数为L( Y , f(x&#xff09;&#xff09;&#xff0c;关系如下&#xff1a; 损失函数&a…

Pyotorch自定义损失函数

&#x1f468;‍&#x1f4bb;作者简介&#xff1a;大数据专业硕士在读&#xff0c;CSDN人工智能领域博客专家&#xff0c;阿里云专家博主&#xff0c;专注大数据与人工智能知识分享&#xff0c;公众号&#xff1a;GoAI的学习小屋&#xff0c;免费分享书籍、简历、导图等资料&a…

GAN的损失函数

1.GAN 在训练过程中&#xff0c;生成器和判别器的目标是相矛盾的&#xff0c;并且这种矛盾可以体现在判别器的判断准确性上。生成器的目标是生成尽量真实的数据&#xff0c;最好能够以假乱真、让判别器判断不出来&#xff0c;因此生成器的学习目标是让判别器上的判断准确性越来…