时间:2021-05-28
JS是单线程的,也就意味着所有任务需要排队,只有当前一个任务结束了,后一个任务才会执行。如果前一个任务耗时很长,后一个任务就不得不一直等着。
Cocos Creator 是采用 Java Script/Type Script语言开发,本质上是JS,同样会拥有以上特征。特别地,如果使用不当,极有可能导致界面卡顿。
比如:在为一个ScrollView的Content创建500个节点的的时候,可能就会出现下面界面卡死的问题
PS:本来加载过程中有一个loading对话框,因为卡死了,就感觉从来没出现
通过阅读本文,你将了解到如何利用「分帧加载」技术解决上述问题,最终效果对比如下:
在正常情况下,我们为ScrollView创建一定数量的子节点的时候,代码可能是这样子的
public directLoad(length: number) { for (let i = 0; i < length; i++) { this._initItem(i); }} private _initItem(itemIndex: number) { let itemNode = cc.instantiate(this.itemPrefab); itemNode.width = this.scrollView.content.width / 10; itemNode.height = itemNode.width; itemNode.parent = this.scrollView.content; itemNode.setPosition(0, 0);}一般而言,当length的值很小,比如10个的时候,程序跑起来的时候,看上去可能会没什么问题,但其实如果仔细一点观察,就发现其实也是会卡死一会,只是很快就结束了。
特别地,如果length的值到一点量级,比如50+个,那么这段代码就会出现上面截图那样子——卡死
归根到底,问题在于通过 cc.instantiate 创建节点以及为这个节点 setParent 时,所需要的时间并没有想象中那么小,当然,也没有想象中那么大。但是当连续创建一定数量的时候,问题就会被放大,也就是说,这个创建节点的时间可能需要一段时间。
可视化一点去理解这个问题的话,恩,大概就是下图这样子
Direct Load
很明显,按照上图,第1到4帧都被完成占用了,导致这期间所有的其他逻辑都会不能执行(Loading对话框出不来,旋转动画卡死等等)。
那么怎么解决呢?
可能有同学第一时间想到用Promise异步解决,但是在这个问题上,Promise只是把红色的这段连续创建节点的代码放到后面一点的时间去执行,但是当红色的代码执行的时候,它依旧会卡死那段时间,所以Promise是不能应对这种场合的。
那么应该怎么解决呢?
其中,一种解决方案,就是我们今天要讲的 「分帧加载」,怎么理解「分帧加载」呢?
惯例,先上图:
Framing Load
配合上图,就比较好理解「分帧加载」了,具体执行过程为
OK,理论说清楚了,那么实际怎么弄呢?
比如:
这个时候,我们需要用到 ES6(ES2015)的协程——Generator,去帮助我们实现。
以我们第二节举例用到的代码(为ScrollView创建一定数量的子节点)为例子,我们将 实现代码为多个小段 以及 分配每一帧的一些时间去执行这些小段 。
拆分前:
public directLoad(length: number) { for (let i = 0; i < length; i++) { this._initItem(i); }} private _initItem(itemIndex: number) { let itemNode = cc.instantiate(this.itemPrefab); itemNode.width = this.scrollView.content.width / 10; itemNode.height = itemNode.width; itemNode.parent = this.scrollView.content; itemNode.setPosition(0, 0);}拆分后:
/** * (新增代码)获取生成子节点的Generator */private *_getItemGenerator(length: number) { for (let i = 0; i < length; i++) { yield this._initItem(i); }} /** * (和拆分前的代码一致) */private _initItem(itemIndex: number) { let itemNode = cc.instantiate(this.itemPrefab); itemNode.width = this.scrollView.content.width / 10; itemNode.height = itemNode.width; itemNode.parent = this.scrollView.content; itemNode.setPosition(0, 0);}这里的原理就是 利用 Generator 将一次 for 循环里创建所有节点,改为拆分 for 循环的每一步为一个小段
当然,这份「拆分后」的代码并不能跑起来,因为它只是实现了拆分步骤,要让它跑起来,我们要上下面的第二段代码
在看一次我们刚才的图
Framing Load
配合图,得出的代码
/** * 实现分帧加载 */async framingLoad(length: number) { await this.executePreFrame(this._getItemGenerator(length), 1);} /** * 分帧执行 Generator 逻辑 * * @param generator 生成器 * @param duration 持续时间(ms) * 每次执行 Generator 的操作时,最长可持续执行时长。 * 假设值为8ms,那么表示1帧(总共16ms)下,分出8ms时间给此逻辑执行 */private executePreFrame(generator: Generator, duration: number) { return new Promise((resolve, reject) => { let gen = generator; // 创建执行函数 let execute = () => { // 执行之前,先记录开始时间戳 let startTime = new Date().getTime(); // 然后一直从 Generator 中获取已经拆分好的代码段出来执行 for (let iter = gen.next(); ; iter = gen.next()) { // 判断是否已经执行完所有 Generator 的小代码段 // 如果是的话,那么就表示任务完成 if (iter == null || iter.done) { resolve(); return; } // 每执行完一段小代码段,都检查一下是否 // 已经超过我们分配给本帧,这些小代码端的最大可执行时间 if (new Date().getTime() - startTime > duration) { // 如果超过了,那么本帧就不在执行,开定时器,让下一帧再执行 this.scheduleOnce(() => { execute(); }); return; } } }; // 运行执行函数 execute(); });}代码中已经附有大量注释,但还是有几个点需要说明一下:
至此,我们一定程度上已经实现了「分帧加载」了~
本项目中所有图示、代码都在Github仓库中,如果需要运行验证,可直接拉下项目即可,不用自己手撸代码验证
👉👉https://github.com/zhitaocai/CocosCreator-ScrollVIewPlus👈👈
以上就是CocosCreator ScrollView优化系列之分帧加载的详细内容,更多关于CocosCreator ScrollView优化分帧加载的资料,请关注其它相关文章!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
以高速摄像机MiroLAB系列为例: M/R/LC110:1630帧/秒; M/R/LC310:3260帧/秒; M/R/LC120:730帧/秒; M
一、简介二、方法1)ScrollView垂直滚动控件使用方法1、在layout布局文件的最外层建立一个ScrollView控件2、在ScrollView控件中加
视频画面本质上是由一帧一帧的连续图像组成的,播放视频其实就是在播放窗口把一系列连续图像按一定的时间间隔一幅幅贴上去实现的。人眼在连续图像的刷新最少达到每秒24帧
flash中帧的4种类型是关键帧、空白关键帧、普通帧和过渡帧。其特点是: 关键帧:时间轴上为“实心的圆点”表示此帧为关键帧,一般需要改变的帧插入关键帧。 普
更精巧的动画设计。动画设计的最初方式是由采用帧格式的图像序列组成,这使得设计好的动画容量较大,加载缓慢,为网站的加载带来灾难性的时间消耗.实际上,对于大多数美工