NGUI的UIScrollview的实现原理

记上次面试被一个主程说,你连NGUI底层探索的欲望都没有,你还说你对编程感兴趣

想想也是,人家代码摆在那给你看你连看都不看,还说自己对学习技术有热忱。


而且当初确实是好奇UIScrollview怎么实现的,所以趁今天刚好做这个需求就看看NGUI底层是怎么实现的。


一、基本结构
首先实现一个UIScrollview基本的结构是

ScrollviewPanel
    -Grid
           -Item

首先在ScrollviewPanel这个控件下挂上UIScrollview这个脚本。unity就会自动帮你挂上UIPanel和Rigidbody这两个component。
这里很重要的是UIScrollview和UIPanel这两个类,这两个就是为什么能只显示固定窗口界面的关键。这个之后再讲。

接着是Grid,Grid上会挂UIGrid这个脚本。这个脚本是负责把它下面挂的所有item有序的排列好,Grid就像一个有多格的收纳盒,每次层的大小是一样的。

最后是Item,Item上挂自己的脚本控制自己的UI摆放。放个BoxCollider作为碰撞(点击)触发,再放一个UIDragScrollview,在Scrollview这一栏上拖入ScrollviewPanel这个控件(没有拖动的话在UI打开的时候系统也会找最近的带有UIScrollview的父节点)。

二、原理
原理比较复杂,涉及到好几块,我们拆分一下,从小的开始。

以上面UI为例

1.首先是每一个item,item上面挂自己控制的脚本,比如删除自己或者其他的什么处理,处理完后要告诉UIScrollview和UIGrid要刷新列表。

2.接着要把中间的多个Item放到一个大框框里,并规定好格子大小,有序的摆放。
这个用UIGrid实现。
UIGrid 代码逻辑相对简单,在UIGrid里,遍历所有item,最顶端item的坐标为(0,0)。根据摆放的方向(横向或竖向),如上图是竖向,根据设定好的Cell Width和Cell Height。计算好下一个的坐标就行了。

2.接下来是UIScrollview,首先按上面说的,ScrollviewPanel上面要先挂上UIPanel,这个简单来说是作为渲染用的,所以当你有这么一块需要特殊处理的模块需要渲染当然需要UIPanel专门控制。
而且在UIPanel上有个Clipping功能。这个功能是用作裁剪用,

也就是中间这一块红色区域,当超框时要做什么处理,是淡化还是直接裁掉还是不处理,

这些东西就在这里设置。
这里就需要好好讲讲。为什么可以让某些item显示某些不显示,某些item只显示一部分。
这里就涉及到UIPanel这个比较底层的东西。

首先我们看UIPanel的LateUpdate函数。

这个每帧更新的函数做了什么操作。这里看到主要就两个
UpdateSelf()和LateUpdatePanel().
首先看UpdateSelf()

这里可以看到。首先各种Update,具体就不说了,说一个UpdateWidget(),为什么要UpdateWidget呢。
这是因为NGUI是根据UIWidget来作为渲染单位,一个UIWidget就一个渲染(这样不就巨多渲染?!所以NGUI会合并Drawcall,这个之后讲)

这也是为什么基本的显示单位向UIlabel和UISprite都继承UIWidget。
这时我们在看看UIWidget脚本,看看它的OnStart()函数。

只做CreatePanel()操作

这里最重要的是

这里说明CreatePanel其实不是真的去Create一个Panel,而是找父节点或者自己节点上的UIPanel,去把自己这个Widget添加到Panel的WidgetList里。


这就是为什么UIPanel被称作Randerer渲染器的原因,一个UIWidget是没有办法渲染的,所有的控件是通过找到UIPanel,再在UIPanel那去渲染。
这也是为什么需要UpdateWidget的原因。

所以回到上面的UpdateSelf()。更新完Widgets后。接着到FillAllDrawcalls()这个函数。
这个函数很重要,就是上面说的如果这么多widget那Drawcall不就要爆了。这个函数就是合并可以合并的drawcall。
(这函数代码太多,截断讲吧)
具体怎么合并的呢。
1.调SortWidgets()排序Widget。

这里可以看到,先比较Depth大小。大的往前排。
如果Depth一样看Material
如果material一样不改变排位,
如果不一样,如果有上一个Material不为空退一位,如果自己不为空进一位。
如果都为空就按实例的id排位。
其实目的就是,把material按前后拍到一起,再按Depth拍到一起。

2.接下来看这个

这段代码是就是负责把drawcall合并。
首先获取上个环节的widgetList,获取每个widget。
评断这个widget的Material,Texture,Shader是否和上一个widget的这些属性是否一样。
如果一样的话就不调SubmitDrawcall这个函数。
接着看这些属性是否为空,如果为空就去Create一个新的Drawcall。
如果不为空就改变这个Drawcall的Depth从哪到哪
把这些数据写到内存。
循环判断。
这样就把用一样的Material,Texture,Shader的widget生成的drawcall合并成一个。


接着看SubmitDrawcall这个函数。

这里我们关注drawCalls.Add(dc);这行代码,这里把这个drawcall加到这个drawcallList里。
这里就把UIPanel的LateUpdate()里的UpdateSelf()基本看完。

接下来就是第二部分LateUpdatePanel()
定位到里面

看到不管是什么渲染队列方式,都是调用UpdateDrawcalls().所以直接看里面。

这里面代码又比较多。主要看这里

简单来说就是各种计算算出这个transform的位置旋转缩放在显示屏中的各种属性数据和这个Drawcall的渲染方式队列裁剪方式裁剪范围等这些数据。

这样就完成了每帧刷新每个widget的transform和drawcall数据刷新。

接下来就要到UIDrawcall.cs里看看渲染。
具体不多说了。我们看为什么那让一些一些widget只显示一部分。也就是解答一开始的问题

为什么可以让某些item显示某些不显示,某些item只显示一部分。

首先我们会看到在之前调用的SubmitDrawcall()中有dc.Set()这个函数。这里会看到最后会到UIDrawcall的CreateMaterial()这个函数里。

在这里进行判断,根据设置好的alpha裁剪还是soft裁剪来选择不同的shader。
而由于这里不同shader会产生不同的动态材质mDynamicMat。

也是由于这个不同的动态材质使得下一步的渲染裁剪得到实现。

我们找到这个函数。(忽略箭头。。。)
这里可以看到下面,当这个widget是需要裁剪的时候会计算上面的动态材质的裁剪坐标和大小,如果是SoftClip再计算强度。

这里就是为什么可以做到 某些item显示某些不显示,某些item只显示一部分。的所有原因了!!!


要了解底层真是不容易啊

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Super ScrollView for UGUI提供基于UGUI ScrollRect的可轻松定制的ScrollView。它是一组C#脚本,可帮助您创建所需的ScrollView。这是非常强大的和高度优化的性能。 文件 Android演示应用程序 演示: - 聊天消息列表演示 - 水平画廊演示 - 垂直画廊演示 - GridView演示 - PageVew演示 - TreeVew演示 - 与稠粘头演示的TreeView - 旋转日期选择器 - 更改项目高度演示 - 下拉刷新演示 - 拉起来加载更多的演示 - 点击加载更多演示 - 选择并删除演示 - GridView删除项目演示 - 顶部到底部的演示 - 自下而上的演示 - 从左到右的演示 - 右侧演示 - 响应GridView演示 - TreeViewWithChildrenIndent演示 特征: - ListView和GridView和TreeView - 无限的项目 - 项目在不同的大小(高度/宽度) - 具有不同预制的物品 - 在初始时间大小未知的项目 - 垂直滚动视图(从上到下,从下到上) - 水平滚动视图(从左到右,从右到左) - 项目填充 - 滚动到指定的项目 - 滚动到具有偏移量的项目 - 项目计数在运行时更改 - 项目大小(高度/宽度)在运行时更改 - 物品捕捉到视口中的任何位置 - 项目循环,如微调 - 添加/删除项目 - 全部删除/删除所有项目 - 刷新并重新加载项目 - 使用池缓存项目,不要在运行时销毁项目 - 有效回收物品 - 平台无关 - UGUI支持 - 支持Unity平台(IOS / Android / Mac / PC / Console / Winphone / WebGL ...)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值