Techeek's Studio.

《欢乐坦克大战》微信小游戏开发总结

字数统计: 2.5k阅读时长: 8 min
2018/01/02

《欢乐坦克大战》微信小游戏开发总结

前言

《欢乐坦克大战》是一款支持实时3V3对战的微信小游戏,可以认为是首批上线微信小游戏中最重度的游戏;游戏复杂度、开发难度、性能挑战也是最大的。项目开发周期非常短,基本是一个月时间完成了单机、网络对战玩法。
客户端开发团队核心成员具有多年cocos2dx引擎的开发经验,所以我们选用的引擎是CocosCreatorV1.6.1,语言是javascript。对于js脚本、微信小游戏平台,开发团队基本是从0开始,边学边做,挑战很大。

架构

网络通信使用了websocket,通讯协议格式是json。根据tdr的xml协议描述,我们自己开发了tdr->json的转换工具。

策划使用excel表格配置数据,也是自己开发了转换工具,将excel表格转换成json文件供客户端读取。

地图没有使用cocos的tilemap,而是自己实现了一个TileMap机制。策划可以在excel中配置地图信息,使用自定义工具转换成json格式的地图文件供客户端加载。

由于开发进度紧张,需要同时进行单机和pvp玩法的开发。我们封装了一个命令层(CMD层),所有战斗逻辑的驱动都通过cmd进行。比如使用摇杆控制坦克运动,是由表现层发送cmd命令,在单机模式下cmd会存储于客户端本地列表,然后由命令管理器CmdMgr在update时读取本地命令列表驱动逻辑层进行处理。而在对战模式中,cmd会被发往服务器,由服务器广播给所有玩家,然后玩家客户端的命令管理器CmdMgr在update时驱动逻辑层进行处理。引入cmd层之后,战斗逻辑层是抽象独立的,不需要关心当前的玩法模式,可以方便的复用,减少了开发成本。

我们pvp实时对战采用的是c/s模式的同步架构,客户端做碰撞检测,将碰撞检测结果通知服务器;服务器进行校验并做伤害计算,然后广播给其他玩家。支持断线重连、客户端crash重连机制。由于服务器拥有战斗中的所有状态数据,重连时将所有数据发送给客户端,客户端进行战斗场景还原。

玩家位置同步采用了基于时间戳的位置点同步算法。这个算法原先应用于《全民飞机大战》的双打模式、对抗模式中,取得了不错的效果,《全民飞机大战》中实时对战采用的是UDP通信。在《欢乐坦克大战的》websocketTCP环境下表现也不错。算法原理如下:

挑战

微信小游戏平台增加了动态执行代码的限制

比如:eval(‘console.log(1)’)、new Function(‘console.log(1)’)、setTimeout(‘console.log(1)’) 等调用方式将失效。

CocosCreatorV1.6.1源码中大量使用了Function,这块经过和cocos引擎开发商的沟通,参考cocos在1.7版本(当时尚未发布)中的修改,修改了源码,解决了此问题。

微信小游戏不允许超过4M

措施1:引擎定制裁剪去掉不必要的模块减少引擎体积

措施2:图片压缩

使用png图片压缩工具pngquant,可以有效的减小png图片的文件大小(通常能压缩60%-70%)。

通过以上2个措施,资源仍然会超标,只能采用资源动态下载的方案了。

措施3:资源动态下载

游戏中增加了一个资源更新场景,游戏启动时,在该场景进行资源更新此时游戏业务模块都没有创建,然后再进行场景切换。等到游戏场景中再进行业务模块的创建和初始化工作。

  1. 先下载一个资源更新配置文件,此文件中有待资源下载列表、资源校验MD5信息。
  2. 根据资源下载列表,将校验MD5和本地文件进行对比,如果相同则不下载,如果不同则下载。
  3. 下载完毕后,进行MD5校验,如果校验不通过则删除本地文件,重新走下载流程。这里的MD5校验,不仅可以校验资源下载是否正确;对于防止资源被恶意修改,资源反作弊也有一定作用。
  4. 修改cocos源码, 在load-pipeline中,将资源读取替换成读取本地的下载文件。

由于游戏运营中可能会有bug发生,需要下发客户端补丁。资源更新配置文件可能会被多次修改,而CDN更新会有延迟问题,导致部分玩家下载的配置文件可能是比较旧的版本。并且有部分中小运营商,为了成本考虑,会缓存旧的文件,以前的项目在发生这种情况时,一般是联系玩家进行定位,发现是运营商问题再反馈给运维同学,由网络部门的同事推动运营商进行修改,效率不高。为了减少这种情况发生的可能性,我们使用了双CDN策略。

具体的做法是,对于同名文件增加版本号机制,更新文件时将文件内部存储的版本号+1,并且在2个不同的CDN都进行更新。客户端下载时,下载2份文件,取版本号大的为准。这样当更新配置文件时,2个不同CDN只要有一个同步到即可,减少了CDN更新延迟,降低了运营商缓存问题出现的概率。

性能优化

和一般的游戏不同的是,微信小游戏平台本身的js脚本执行效率较弱,iOS环境小游戏javascript引擎目前使用的是JavaScriptCore,默认没开jit优化,js执行速度会比手机safari慢,从简单测试结果来看,速度慢两倍左右。从profiler来看,js脚本执行时间会占到80%左右。因此减少脚本的计算量也是性能优化一个重要的方面。

小米6 android小游戏 android 微信浏览器 android chrome
57.55 53 58
iphone6 IOS小游戏 IOS 微信浏览器 IOS safari
20 48 50

帧率测试对比

通过统计分析,游戏中的消耗占比

性能开销 占比
场景遍历 visit 31.20%
更新变换数据 transfom 27.80%
渲染 rendering 9.10%
游戏实体update(玩家、子弹) 4.20%
动画更新 anim update 3.40%
碰撞物件更新 updateCollider 3.20%
触摸事件处理 2.80%
摄像机、坦克UI、特效坐标跟随坦克更新 1.40%
AI更新 1.30%
碰撞逻辑回调 _doCollide 0.50%
DrawCall

渲染批次合并和大多游戏项目类似。需要合理的规划图集的使用,将同一个层次的GameObj使用的图片资源进行拼图。

可以分为地图背景层、地表、地图物件、坦克、子弹、特效、UI等拼图,尽量确保同一个层次的游戏对象使用相同的图集,相邻的精灵使用的材质相同。

mask

游戏中会显示玩家的圆形头像,而微信平台下载的头像是矩形。原先头像显示使用的是cocos的mask组件,效率较低。我们自己实现了一个基于mesh的控件,把一个圆等分为n个三角形,给这些三角形顶点赋予相应的UV,从而画出一个圆形头像。减少了头像渲染时的批次开销。

碰撞检测

cocos creator自带的碰撞系统效率不高,没有做空间划分,不适合大量单位的碰撞检测。并且每帧都更新碰撞体的碰撞盒。而我们游戏地图中存在大量的静态物件(地图中的砖块、主基地、钢板等),并且玩家在场景中移动时,是通过移动摄像机达到地图视野的变化,所以大量的地图静态物件的世界坐标是不变的,他们的碰撞盒只需要计算一次即可。

这里我们给cocos的node增加了一个属性static,static节点的计算结果可以缓存起来,避免重复计算。

对象池

游戏中的坦克、子弹、砖块等采用对象池,进入战斗场景时进行足够数量的预加载,战斗过程中进行复用,避免实时的对象创建与销毁。

避免场景、节点更新

分析cocoscreator的源码发现,当有节点发生active\deactive\setparent调用时,会触发递归遍历场景,开销较大。

为了避免这类开销,游戏中的物体死亡时,不会将其从场景中移除或者禁用;而是设置死亡状态,通过移动坐标到很远的地方,代码中不执行相应的逻辑处理。尽量保持帧率平稳,避免性能曲线的毛刺

裁剪

不在主角视野范围内的,并且不是持久播放的特效和声音可以进行裁剪不播放。

机型适配

对于美术资源进行了高、中、低3档分级,由策划在资源表格中配置不同分级下的资源名称。游戏过程中,根据机型和实际性能表现,选择一种档次进行表现。

图中横坐标是时间(单位秒),纵坐标是FPS,可以看出FPS有了明显提升。通过一系列的优化措施,最终保证了低端机iphone5S基本能满足游戏需要。

CATALOG
  1. 1. 《欢乐坦克大战》微信小游戏开发总结
    1. 1.1. 前言
    2. 1.2. 架构
    3. 1.3. 挑战
      1. 1.3.1. 微信小游戏平台增加了动态执行代码的限制
      2. 1.3.2. 微信小游戏不允许超过4M
        1. 1.3.2.1. 措施1:引擎定制裁剪去掉不必要的模块减少引擎体积
        2. 1.3.2.2. 措施2:图片压缩
        3. 1.3.2.3. 措施3:资源动态下载
      3. 1.3.3. 性能优化
        1. 1.3.3.0.1. DrawCall
        2. 1.3.3.0.2. mask
        3. 1.3.3.0.3. 碰撞检测
        4. 1.3.3.0.4. 对象池
        5. 1.3.3.0.5. 避免场景、节点更新
        6. 1.3.3.0.6. 裁剪
        7. 1.3.3.0.7. 机型适配