创意小游戏《动物餐厅》技术总结以及现存问题反馈

最早接触cocos 是12年刚实习那会,依稀记得那时候cocos2d-x 的版本号还是1.x,一路走过来cocos已经陪伴我走过了8年风雨。很感谢cocos以及类似开源软件给我带来的长足技术进步,一直在社区里也是沉默寡言,基本只对bug讨论和feature感兴趣【可能因为时间关系,更多关注的是自己团队实际遇到的问题】

深感闭门造车的困境,终于趁着有半日的喘息,一股脑把《动物餐厅》开发到至今遇到的难点问题进行一次总结反馈,也算是对cocos社区的新一轮反馈与回馈

首先从最严重的版本bug说起

2.0.10的微信下载有一个严重的bug,触发概率不高,但是一旦引发这个bug,会导致某些资源的永远无法被正确下载和使用。我是一个比较激进的开发者,所以6月3号左右我就更新了2.0.10,并且在经过1天测试后,发布到了线上版本,线上版本也经过了一轮灰度测试,才全量更新,但是从6月5号开始,我们逐渐收到反馈,玩家无法进入游戏,而且数量在逐渐增加,而且我的经验告诉我,如果有一个玩家发生了这个bug,并且告知了我们,那么就一定有100个玩家也遇到了类似的问题,但是他们可能没法或者不愿意联系我们。因为我们业务代码并没有改动启动场景相关逻辑,于是我马上着手开始研究引擎的相关改动。对于线上玩家解决的方式是删掉小游戏【但是单机小游戏不能这样做,因为也意味着丢档】。经过了小几个版本的迭代测试,我基本已经定位了问题,并于昨天发布了正式修复版本,这几天也经历了非常非常难熬的DAU持续的可怕下滑。

问题在于新的wx-downloader使用了记录文件缓存的方式去判断是否已经缓存好某些资源,这个思路没啥问题,而且可以很好的解决空间超过50M的清理问题,但是问题在于这部分的代码是有问题的,维护这个文件缓存的几处关键代码,并没有处理好并发写的问题,例如在cleanCache()这个函数里,使用了writeFileSync,去保证文件是同步写入的,但是这里忘记考虑了同时writeCacheFile在异步的写入文件。并且writeCacheFile也没有考虑等待上一次异步写完成才执行新的写入,当然这种写法会出现问题也只是我的YY,于是我尝试加上了一个安全性检测,就算是cachedFiles里记录的缓存文件,我也会去本地判断一次是否存在。几个无法进入游戏【卡在加载界面】的玩家使用了新版本后,确实解决了卡加载的问题。

并且经过我的review,我发现wx-downloader并没有处理相同请求问题,也就是对于相同的资源请求,在成功下载之前,wx-downloader会重复下载,从而造成效率和带宽浪费。于是也顺手优化了一下,经过这几天的线上测试,目前反馈良好。修复的代码我会放到附件里,大家谨慎使用。

2.0.10fix.zip (7.6 KB)

我们明天还会更新一个版本,有资源变动,我会根据实际用户反馈来跟进这个bug的修复情况

根据大家反馈,我发现2.1.12 有修复这个问题,但是和我修复的方向不太一样,引擎组的修复的应该是另外一个问题,我打算合并到线上版本

但是还是建议参考我的代码
1.我觉得重复的资源实在没必要去下载,毕竟CDN还是有点贵的 0.0
2.我们线上版本的用户不删除小游戏是会永远卡在某个资源的加载上,所以我仔细对比后,还是应该保留我修改的代码

然后是困恼了我几个月的内存释放bug

首先极力吐槽一下loadRes和releaseRes的设计完全没法使用,在我看来,这种new 和 delete 成对使用的东西,本身就已经具有一定的风险和难度,但是在creator里面releaseRes是根本无法使用的api,因为releaseRes会释放所有依赖资源,而不去管这部分的资源可能被别的资源引用,举个例子:loadRes(“aaaa”); loadRes(“bbb”) releaseRes(“aaa”),如果bbb这个预制体或者其他复合资源,引用到了aaa或者aaa里面的某个依赖资源,那么在释放aaa后,bbb就会直接出错。我能理解出错的原因,但是如果连这种成对使用习惯都不能保证正确性,那么这个api我想不到任何的用处。正式项目里,要多么小型的游戏才可以有资源相互独立的情况?

那么我绕过这个坑的办法是,通过getDependsRecursively得到资源的所有加载资源,并且对这些资源自己做引用计数,保证资源不被错误的施放。好的,这个姿势看起来很正确。但是我依然会不断收各种姿势报错,因为资源还是会被错误的释放掉,我深入看了一下cc.loader,联想了一下creator的复合资源设计,发现一个很大的问题,举个例子:如果我正在加载资源AAA,资源AAA依赖了资源BBB,CCC,DDD,EEE等等,另外一处业务逻辑加载了资源BBB,比方因为界面被关掉,导致此时业务层BBB的引用计数是0,又因为AAA要加载很多资源,还没有调用load onComplete,于是BBB被释放后,悲剧再次发生。

于是再次改造,在加载资源的完成进度函数里,对每一个完整的子资源进行保护性引用,错误次数明显减少,但是依然会报错【解释一下报错对于玩家而言,就是纹理错乱,或者出现黑框框,或者有些资源加载不出来】。分析了一下,我觉得是因为子类资源也可能有很多子类资源,而且子孙类资源可能会因为网络问题暂时加载失败,导致这个子类资源本身是加载失败的,那么同样的问题,这些资源因为无法及时通知到我的业务层,我也没法对他们进行计数引用保护。因此到现在为止,我们的游戏还是会收到类似的问题,只是比3个月前的版本频率降低了很多。

划重点,这里急需引擎组帮助

所以我认为,基于现在的引擎设计,我在业务层已经包裹了非常复杂的逻辑还是无法避免错误释放资源的问题,而且因为cc.loader的下载行为是依赖其他抽象类,具体的下载逻辑是在各个平台分别实现的,我也暂时没能力去修改引擎层,急需要引擎组提供一个可行方案,或者希望引擎组能修改内部的机制来完善资源释放问题,例如可以在引擎内部去做这个索引计数?

效率优化问题

1.在小游戏端,你一定要告别系统字体,必须用bmfont等方案代替,因为系统字体的渲染效率在部分机型上令人发指,当然这个可能微信背锅的成分比较大
2.在小游戏端,你基本也告别了shrink,因为在某些情况下,一个label的shrink就可能造成成吨的卡顿
3.关于scrollView的性能问题,我们几乎所有的scrollView都做了三个优化工作:1.分帧创建初始item 2.根据需要创建其他的item 3.不可见的item隐藏起来,active = false,但是你要注意兼容layout
4.类似于第3点,对于大量实例化的预制体都应该尽量采用分帧创建,在顺畅体验和及时体验【卡顿一会全部东西出来】上做一个平衡取舍
5.优化渲染效率,禁用那些在屏幕外确定不可见的render组件,更好的做法是如果你的代码是视图与逻辑分离的,那么把这些节点都设置为active = false状态

首场景加载问题,

我们的首页场景包含了一些资源,我注意极个别玩家第一次进游戏卡黑屏或者白屏,当遇到这个问题的玩家数量越来越大时,我去研究了一下启动源码,发现启动相关的代码并没有考虑加载失败的问题【因为超过4M的游戏,会把res里的文件全部删掉,这就包括了首页相关的资源】,那么加载首页的时候,如果恰好遇到网络问题,那么首页因为没有重新加载资源的机制,就导致了玩家卡在首页黑屏或者白屏状态【因为缺少首页资源】

我目前解决的方案有两个:1.利用脚本每次保留首页相关的资源 2.开启了设置里的合并首包资源选项

第一点难度很大,我们的脚本比较挫,需要一定的人力干涉
第二点我近期才开始测试,具体效果还未知【而且oppo好像并没有这个选项】

资源过于分散的问题

现在基本每个原生资源都对应一个json,这样的设计其实在小游戏平台特别不友好,增加了很多网络请求,如果能够把一些json合并起来,请求次数应该可以有效减少,当然我知道这个平衡难度比较大,不过一些简单的策略应该可以起到很高的性价比,例如定一个Max尺寸,例如50k,对于小于50k的json进行递归合并,直到只剩一个json或者没有小于50k可合并的操作。

编辑器的一些bug

每次我build 其他版本,编辑器总是把ios的脚本加密和sdkBox自动设置成false,很苦恼
每次build oppo版本,包体会越来越大,因为logo的自动命名问题,每次build都会新增一个Logo和配置文件

ios锁屏唤起后卡死问题

这个问题很多帖子有提及,这里就不做累赘,而且也已经列入到引擎组修复计划中

注意事项

以上的总结都是基于cocos creator 2.0.10,更多的针对小游戏类平台,也是现在版本还存在的问题。我知道cocos 或者 creator也正在遭遇很多问题,但是看到你们还在努力维护版本,维护社区,作为一个技术粉,我还是感到很开心,之后也会更多的抽出时间加入社区讨论,共同为大家做贡献。

新增内容补充
1.关于手Q打包问题,目前手Q游戏有点混乱,我采用的方式是直接使用微信build包,然后你啥也不用修改,就可以运行在手Q开发工具,当然如果你和我们一样采用了诸多微信能力,那么例如客服和游戏圈等能力是手Q不支持的。你还是需要写点适配

2.如何部署可扩容的服务器?我的建议是如果你是个人开发者,初期可以不考虑这个问题,但是你需要使用腾讯云或者阿里云来部署你的服务,在游戏量级大起来的前夕,你需要部署负载均衡,购买db存储服务来解决未来的高压问题,除了数据需要写点代码迁移外,其他部署起来是很快的

3.注意一定要使用CDN,不然你的游戏加载速度可能慢到天际,游戏量级小的时候按流量计费价格也完全可以接受

4.注意分包加载微信端是有问题的,分包有一定概率不会回调,我多个版本用肉身验证过,这个问题也反馈给了微信团队,暂时没有收到解决的消息

最后的最后,小小的打个广告

48赞

字能不能大点,看着眼疼:joy:

很有情怀,感觉类似玩法的游戏不少,我个人还是喜欢那种简单粗暴的游戏

1赞

等附件

等附件

哈哈哈

已经上传

已经上传了哦

哈哈哈 我不知道咋个调整字体大小 我稍微分割了一下段落

的确 所以这个游戏女生居多

现在貌似每一张图片资源都对应一个json文件。 这个修改感觉有点鸡肋啊 !!!!!

mark

问一下,动物餐厅总的开发时间有多长?

我这几天也碰到了这个问题

所以会有2.0.11来修复这个严重的问题吗

会的 213455

感谢楼主分享,还带来这么多的解决方案,很赞。

对于微信小游戏端的文本问题说明:

  1. 文本是通过canvas绘制然后作为字体纹理提交进行文本的渲染,卡顿的根本原因是在微信端同帧调用texImage2D次数较多时的性能消耗巨大。

  2. Shrink模式会根据文本内容不断调整文本的Size进行重排并重新进行绘制以及纹理的提交,造成卡顿的原因一是跟问题1中同样的texImage2D调用次数的增加,另外就是Shrink模式本身存在一定的计算量。

如何解决:

  1. 对于单个界面创建时,如果存在大量的文本内容,但是文本内容的字符重复较多时(字符数量 < 文本数量)可以采用CHAR模式进行字符缓存,减少字符纹理的创建。如果系统文本数量太多,部分采用BMFont或者第三方字体解决方案的同时,其他系统文本可以分帧延迟创建,尽量避免同一事件创建大量的文本数量。

  2. SHRINK模式主要是文本持续变化导致的文本重绘,这个我们后续会对SHRINK的计算进行优化,对于重绘导致的texImage2D次数增加,可以尽量控制节点的Size,减少文字大小变化带来的重排消耗。

1赞

请问有没有比较详细的重现步骤或者是demo?

这个 2.0.10 应该修复了呀