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
共同上图,就较量好领略「分帧加载」了,详细执行进程为
先将耗时卡死的代码拆分为许多小段
然后每一帧,分派一点时间去执行这些小段
这样子一来,每一帧,我们就留了时间给其他逻辑去跑(那么Loading对话框也可以出来了,旋动弹画也可以继承了)
OK,理论说清楚了,那么实际怎么弄呢?
好比:
怎么拆分代码为许多小段?
怎么分派每一帧的一些时间去执行这些小段呢?
这个时候,我们需要用到 ES6(ES2015)的协程——Generator,去辅佐我们实现。
四、办理方案(代码篇)以我们第二节举例用到的代码(为ScrollView建设必然数量的子节点)为例子,我们将 实现代码为多个小段 以及 分派每一帧的一些时间去执行这些小段 。
4.1 操作 Generator 将代码拆分为多个小段拆分前:
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 轮回的每一步为一个小段