TGideas-腾讯互动娱乐创意设计团队

Egret开发微信小游戏的性能优化总结

Egret开发的微信小游戏,在Android设备上显示开放数据域时严重丢帧,怎么破。换引擎?修改实现逻辑?还是跟需求方说取消这个特性?有兴趣就戳进来看看吧。

随着小程序/小游戏的盛行,越来越多的游戏业务选择用新的营销形式为游戏拉新圈粉,这对传统的前端/重构在技能的广度上也有了进一步挑战。《雪鹰领主》同名小游戏是团队开发的第一款微信小游戏,由于是首次接触小游戏,在开发之前抽空对多个引擎进行调研和分析,但依然踩到了一个大坑。接下来就一起回顾下项目遇到的性能问题和对应的解决过程。

项目接近尾声的时候,在Android真机上测试发现,调用了子域(开放数据域)的游戏场景丢帧非常严重(后文中的子域特指微信的开放数据域),平均帧率10FPS,所有道具的位置渲染错误而堆积在一起,没法正常体验游戏。具体情形如图:

 

 

(左图表现正常; 右图卡顿导致道具堆积)

 

经过一段时间的不断摸索和测试,终于总结出一套颇为稳定的解决方案。解决方案固然重要,但个人觉得其背后的探索和试错过程也是一次宝贵的历练。具体调优过程将在下面一一列出。

1. 开放数据域丢帧优化

1.1 从自身找原因

由于是首次使用egret引擎,所以在使用引擎方面可能有不得要领的地方,于是我仔细翻阅子域的实现代码,以期待能从中发现一丝端倪。

这是子域的实现代码:

 


 

子域的实现非常简单,不断对比玩家的实时分数和好友的分数,如果满足条件则展示超越的好友。然后看Egret中主域调用子域代码:

 


 

同样简洁的代码,没有奇技淫巧的逻辑,除了最后一部分。egret中的startTick是以60fps去执行回调函数,回调中的删除webgl纹理的实现,也就是普通的删除操作,没有发现异常,而且大致搜索源码后,也会发现其他地方也经常使用到这个方法。

我尝试性的对这部分代码进行了测试:

  1. 当我删除了这部分代码后,游戏不丢帧了,然而子域也消失了,显然这不满足需求。
  2. 当我只删除deleteWebGLTexture这行代码时,发现展示子域的时候,场景中同时出现了其他的图片素材,猜测Egret引擎可能把游戏中所有的图片元素缓存到sharedCanvas了。所以在绘图子域的时候,要不断的去删除纹理。
  3. 由于startTick函数是以60FPS进行的高频操作,难道是高频操作导致丢帧吗?我尝试切换为自定义的定时器低频操作,然而并没有改善。

引擎自身实现,主域调用子域的方式以及子域的实现,这几个环节糅合在一起,一时间难以定位。我把代码回顾到原始版本,然后把子域的逻辑和UI绘制代码清空后,发现游戏场景中依然非常卡。推测丢帧的问题跟逻辑没太大关系,可能是引擎导致的。

1.2 向网络求助

有人说,你遇到的问题,99%的都是别人已经解决过的问题。带着这份信心,我在官方论坛上搜索了一番。幸运的是也有人遇到了同样的问题。悲剧的是官方并没有回复过这个问题 ,瞧:

 

1.3 向业界同仁求助

深圳的同事近期也有开发小游戏的经验,但他们并没有使用过Egret引擎,而是采用了Cocos或其他引擎,而且部分子域是使用原生JS实现,虽然没有得到一个完整的解决方案,但是我们有了一个共识的感受,即在主域中去频繁调用子域(子域自身也在持续渲染),本质就会很卡顿。(感谢DG、桂花、沙鹰、玉环、子聪等多位同事的答疑)

 

1.4 探索 + 试对

此时离提交测试版本只有一个星期不到了,仍是一筹莫展,于是主动跟需求方勾兑了项目遇到的难点和我们尝试过的努力,需求方也表示实在不行也能接受“在Android中取消该特性”作为兜底方案。虽然有了定心丸,但教练说,“现在放弃就等于输了整个比赛”。

回到想第一阶段中推测引擎实现子域可能会存在问题,于是下决心尝试用原生JS实现子域(因为子域除了超越好友,还有排行榜页面,考虑到重构这块的工作量和重构后未知的预期,一直没去尝试用原生JS重写)。

然而,当我用原生JS写完超越好友的Demo后,在真机中测试,性能有了一定的改善(帧率 25FPS左右),但是依然达不到流畅游戏的标准。但至少有了一个方向,与此同时,也有资料表明可以通过尝试切换渲染模式为canvas来改善性能。于是便针对这渲染模式和子域实现方式两个维度进行组合测试。测试结果分组如下:

 

渲染模式\子域实现方式 引擎实现

原生JS实现

webgl(默认) 10fps 25fps
canvas 35fps 55fps


经过多重组合调试,终于有了令人欣慰的结果,为了保证我不是眼花,立马找到身边的Android测试机器,并让同事和需求方测试新的改进方案,大家一致认可了这个方案。此时,重写整个子域模块的畏难心理也消失殆尽了(友谊的小船,我用JS也能划 )。

 

1.5 重构开放数据域

Egret引擎帮开发者解决了各种设备的适配问题,而原生JS中的canvas在绘图时,会由于设备尺寸和的DPR的不一致导致最终绘制的表现也有差异。为了保证原生canvas绘制的UI能兼容Egret引擎绘制的主域UI。这里Alex对canvas2d的context做了封装处理,以保证开发者可以复用原有UI的尺寸参数,提升重构效率。

 


 

回顾解决子域性能的过程,经历了自己摸索推测,论坛/同事咨询套路,再回到推测、实践、组合求证。 种种不易,所幸最终找到了合适的解决方案。后续Alex会将Egret开发小游戏的boilerplate开源分享给有需要的同事;对于引擎测的问题,也第一时间和微信的同事进行了沟通和反馈,官方表示后续联系Egret官方团队进行优化。

 

2. 主域性能优化

在反复测试的过程中,我们也发现了游戏场景中偶尔会出现丢帧的现象。我们通过performance面板监测后,发现是由于一些异步请求导致了JS执行时间较长从而导致了丢帧(熟悉性能优化的同学都清楚小游戏性能就是跟16ms赛跑,原理我不赘述)。

图中红色区域的峰刺就是js执行耗时较长的部分,这会导致丢帧,展开耗时部分的执行栈:

 

 

经过逐层剥离发现,耗时的部分是发生在调用微信本地缓存的接口,经过和微信同事的求证,这个prompt接口用于小游戏和微信底层进行通信,根据环境的差异耗时会略有差异,1次同行平均时间5-10ms,一个本地存储的读或写需要四次prompt通信,所以前台就会看到1~2丢帧。这是由于底层实现决定的,基于这个不可抗拒的原理,我们对逻辑进行了优化,尽量避开了在高频场景中去调用本地存储。下图是优化之后的性能监测图:

 

 

虽然最终通过使用原生JS重构子域,并调整渲染模式的方式解决了性能问题。但更底层的原理,还需要我们去深度分析Egret引擎的源码实现,另外也希望微信同事尽快推进Egret在这方面的改进,以让开发者可以更专注于业务逻辑,提升开发效率。

 

(【打赏二维码】https://shp.qpic.cn/cfwebcap/0/ff7b883771d452c8b26f4062cbdad1b7/0?width=200&height=200)

 

求经验
关闭

分享

返回顶部