【世界播资讯】28.从chrome消息队列谈unity3d的UI框架设计


【资料图】

1 缘起

在UI架构圈,MVC是一个著名的小团伙,其中M是被偷窥重度成瘾者,不过我们一般称之为观察者,毕竟读书人的事,怎么能叫偷呢对吧。

M喜欢散播自己的花边小新闻,屁大点儿事,都会翻译成小道消息重大事件散播出去。VC则是俩狗仔,不管M出了啥事,总能第一时间闻到消息,摇旗呐喊。

这种玩法同时满足了M和狗仔们的大多数需求,因此很快在UI架构圈流行开来。去年我用unity3d设计过一版UI框架,也是基于MVC模式。

本来这样也挺好,M出自己的名,狗仔追自己的星。但M太秀了,有时候一股脑爆出一大波新闻,让狗仔们忙得应接不暇,有时候又波澜不惊,让狗仔们急得抓耳挠腮。

MVC模式中,M抛出的事件默认都是同步处理的。这带来了一些问题:如果同一帧内收到并处理太多事件的话,可能由于处理时间太长,导致游戏卡顿。

2 取经

最近的学习研究chrome浏览器的一些架构设计,发现跟unity3d游戏设计有一些相似相溶的地方,随手做了一番对比:

Unity3dChrome
消息队列消息队列
定时任务setTimeout(), 延迟队列
观察者MutationObserver
Task/CoroutinePromise
async/awaitasync/await
游戏渲染帧requestAnimationFrame()

早期的js对dom事件的支持并不友好,只能通过setTimeout()轮询处理。这种方式简单粗暴,但也有一些问题:如果轮询间隔时间过短,则大部分情况下CPU在空转;而如果轮询间隔时间过长,则dom事件会得不到及时响应。

2000年的时候js引入了Mutation Event,采用观察者模式,当dom发生变化时立刻触发相应事件。Mutation Event属于同步回调模式,解决了实时响应的问题,但也为页面展示带来了性能隐患。当我们使用js大量修改dom结构时,会频繁抛出事件,如果所有的dom事件都同步处理,势必会导致页面卡顿。

也因此,Mutation Event被反对使用,并逐渐从web标准事件中被移除。dom4之后推荐使用MutationObserver,其作出的改进其实主要就一点:把所有的dom事件统一放入到微任务消息队列中,在每一帧的结尾一次性触发。通过异步回调解决性能问题,通过微任务解决实时性问题。

3 消息队列

chrome中的异步任务体系都是基于消息队列完成的,包括dom事件、fetch网络请求、promise异步任务等等。

js中的异步任务还细分为了宏任务与微任务,其中只有MutationObserver 和promise是微任务,其它的像setTimeout()之类的都是宏任务。

宏任务由宿主(比如chrome中的window对象)维护,但js引擎需要有自己的异步任务体系,因此引入了微任务队列。

后端也有消息队列,比如Kafka、RocketMQ等,但与chrome把消息队列主要用于异步任务处理不同,后端消息队列最重要的作用主要有三:定序 (Ordering),分离式数据库 (Unbundling Database)、重放 (Replay)。以首字母可简记为:OUR。

我们在做unity3d框架设计的时候,其实也有大量使用消息队列用于处理异步任务。

比如处理网络协议:通常的做法是单独起一个网络线程,接收网络协议,并把它们放到消息队列中,然后由主线程按顺序异步处理这些网络协议。

再比如Coroutine库:可以把所有的协程对象按顺序放入到一个消息队列中,每一帧按顺序遍历它们,并调用它们的next()方法。

这中间还诞生过一个很有趣的技巧:放置于消息队列中的消息,往往对先后顺序有要求,但对处理时间并不敏感。如果发现当前帧已经占用了太长的时间,可以直接跳出当前帧处理过程,把剩余的消息推迟到下一帧去处理。即无需每一帧都清空当前消息队列,从而可以缓解游戏卡顿的问题。我把这个小技巧称为分帧。

4 缘落

但UI框架遇到的问题又略有不同。游戏中有很多非常复杂的UI,比如背包,在加载结束之后,需要按需初始化大量的控件。初始化控件的逻辑是游戏业务逻辑,而不是框架底层逻辑,因此很难完全封装在框架中。另外,初始化过程通常是在一帧内完成的,数量越多越有可能导致游戏卡顿。

针对这个问题,通用优化方案是将这些控件预先拖到一个MonoBehaviour脚本中,从而在加载的时候同步完成初始化过程。这个方案不是本文的重点,因此不再更多展开。另外,这个方案只能解决静态控件的初始化问题,对于背包这种需要根据服务器协议初始化大量动态控件的复杂UI起不到优化效果。

基于前述消息队列和分帧的思想,再参考MonoBehaviour的Start()方法可以配置成协程,我这里提一个新的优化思路:把UI框架中用于控件初始化的OnLoaded()方法设计为协程,这样当遇到大量控件需要初始化的时候,可以支持分帧处理,从而缓解因为大量初始化动态控件导致的卡帧。

这个设计会带来一些新的问题,其中一个是:很多UI事件会有先后顺序要求,比如UI框架中的OnOpened()方法应该在OnLoaded()方法之后回调。但将OnLoaded()方法修改为协程后,就可能发生OnLoaded()方法未完全回调完成,就回调到了OnOpened()方法的情况。这需要进一步调整UI框架逻辑,以约束UI事件之间的先后顺序关系。

5 缘尽

相逢是缘,相知是福,再不关注,就真的缘尽了老板。

5 References

本文提到的UI框架位于unicorn库中,unicorn-examples示例代码

18 | 宏任务和微任务:不是所有任务都是一个待遇

关键词: 网络协议 动态控件 轮询间隔

为您推荐

【世界播资讯】28.从chrome消息队列谈unity3d的UI框架设计

1缘起在UI架构圈,MVC是一个著名的小团伙,其中M是被偷窥重度成瘾者,不过我们一般称之为观察者,毕竟读书人的事,怎么能叫偷呢对吧。M喜欢散

来源:程序员客栈2023-03-04

8+8除以0等于几_8除以0等于多少

1、1、这个题目不成立。2、2、因为零不可以做除数。3、3、所以这个题是个错题。4、4、不过0可以作为被除数,0

来源:互联网2023-03-04

设"司机之家”、母婴室、便利店,新阳高速潭水南加油站正式投入运营

设 "司机之家”、母婴室、便利店,新阳高速潭水南加油站正式投入运营

来源:北京青年报官网2023-03-04

观察:社保卡线上线下并行服务显效能

中国消费者报讯(记者聂国春)人力资源和社会保障部副部长李忠3月2日在国新办发布会上表示,目前,电子社保卡领用人数已经超过7 4亿人,与实体

来源:中国消费者报2023-03-04

约基奇刷数据?马龙:也许有人对他审美疲劳了 他是纯粹的篮球手

约基奇刷数据?马龙:也许有人对他审美疲劳了他是纯粹的篮球手直播吧3月3日讯NBA官方公布了二月的月最佳球员,约基奇当选西部周最佳。谈到外界

来源:直播吧2023-03-04

海纳百川有容乃大是什么意思?_海纳百川有容乃大是什么意思

1、1、出处:: "壁立万韧,无欲则刚;海纳百川,有容乃大。2、2、 "林则徐解释:有容乃大是指你要拥有宽厚的胸襟和积极好学

来源:互联网2023-03-04

3月2日香港银行间同业拆借利率(Hibor) 环球精选

报价品种名称中国货币市场基准利率(%)涨跌(基点)O N1 97179-27 841W2 51702-4 802W2 66262-2 771M3 03720-0 402M3 428810 113M3 807930 036M4 20881-0

来源:同花顺金融研究中心2023-03-02

211文科硕士吐槽“招聘会均薪5500元”惹争议 机构调研显示“文科就业签约率约12%”

211文科硕士吐槽“招聘会均薪5500元”惹争议,机构调研显示“文科就业签约率约12%”,怎么破?

来源:每日经济新闻2023-03-02