1 简介
《欢乐坦克大战》是一款支持 3V3 实时对战的作品,也是最早上线的微信小游戏之一。由于这款游戏在微信小游戏中属于重度作品,项目开发周期很短,因此游戏复杂度、开发难度、性能挑战也颇大;项目团队在一个月内就完成了单机和联网对战玩法的开发。
同时,由于客户端开发团队的核心成员都有多年的 Cocos2d-x 引擎开发经验,项目组决定使用 Cocos Creator v1.6.1 版本引擎进行开发。而对于微信小游戏平台使用的 JavaScript 语言,开发团队基本都是从零开始,边学边做,这对他们自己来说是一个很大的挑战。
2 架构
在网络通信方面,项目采用WebSocket协议进行通信,通信格式为JSON。为了迎合TDR的XML协议,项目组开发了TDR->JSON转换工具。
为了方便规划人员使用Excel表格进行数据配置,项目组还开发了将Excel转换为JSON文件的工具,以便客户端读取配置文件。
对于地图,我们没有使用 Cocos 引擎自带的 TileMap,而是自己实现了类似 TileMap 的机制。规划人员可以在 Excel 中配置地图信息,并使用工具将 Excel 转换成 json 格式的地图文件,供客户端加载。
由于开发工期紧张,我们需要同时开发单机和PVP两种模式。因此我们封装了一个命令层(CMD层)来驱动战斗逻辑。例如,使用摇杆控制坦克移动时,展现层会将CMD命令发送给逻辑层处理。在单机模式下,CMD会存放在客户端的本地列表中,然后在Update时由命令管理器CMDMgr读取本地的命令列表来驱动逻辑层进行处理。在战斗模式下,CMD命令会发送到服务器,由服务器广播给所有玩家,在Update时由玩家客户端的命令管理器CMDMgr驱动逻辑层进行处理。引入命令层(CMD层)之后,战斗逻辑层抽象而独立,开发者无需关心当前的玩法模式,并且可以轻松复用,降低开发成本。
我们的PVP实时战斗采用c/s模式的同步架构,客户端进行碰撞检测,并将碰撞检测结果通知服务器,服务器验证并计算伤害,然后广播给其他玩家。游戏支持断线重连和客户端崩溃重连机制,服务器拥有战斗中的所有状态数据,重连时将所有数据发送给客户端,客户端还原战斗场景。
玩家位置同步采用基于时间戳的位置点同步算法,该算法原先在《空战》的双打模式和对抗模式中使用,《空战》中的实时战斗采用的是UDP通信,而在《欢乐坦克大战》的WebSocketTCP环境中也取得了不错的效果。算法原理如下:
3. 挑战
在开发过程中,我们遇到了很多挑战,但我们都一一解决了,具体遇到的问题如下:
1.微信小游戏平台增加动态代码执行限制
微信小游戏平台增加了对动态代码执行的限制,如:eval('console.log(1)')、new Function('console.log(1)')、setTimeout('console.log(1)')等调用方法无法调用。在 Cocos Creator v1.6.1 源码中,Function 被大量使用。为了解决这个问题,我们与 cocos 引擎开发者沟通,并参考 cocos 1.7 版本(当时尚未发布)的改动,修改了部分源码,解决了这个问题。
2.微信小游戏不得超过4M
正如标题所示,微信小程序对尺寸的要求非常严格。为了解决这个问题,我们想出了很多解决方案。
措施一:定制引擎游戏开发,去掉不必要的模块,减小引擎体积,可以通过设置引擎模块来实现。
步骤2:图像压缩
使用png图片压缩工具pngquant,可以有效减少png图片的文件大小(通常减少60%-70%左右)。
即使采用以上两种措施,资源仍然会超出限制,所以只能采用动态资源下载的解决办法。
措施三:资源动态下载
我们给游戏添加了资源更新场景,游戏启动时,场景更新资源时不会创建游戏业务模块,而是在游戏场景中创建并初始化业务模块,然后再进行场景切换,具体方案如下:
1.首先下载一个资源更新配置文件,里面包含了需要下载的资源列表以及资源验证MD5信息。
2、根据资源下载列表,将验证MD5与本地文件进行对比,相同则不下载,不同则下载。
3.下载完成后进行MD5校验,如果校验不通过,则删除本地文件,重新开始下载过程。这里的MD5校验不仅可以验证资源下载是否正确,还可以防止资源被恶意修改,资源防作弊。
4、修改cocos引擎源码,在load-pipeline中将资源读取替换为读取本地下载的文件。
由于游戏运行过程中可能会出现 Bug,需要发布客户端补丁。资源更新配置文件可能会被多次修改,CDN 更新可能会有延迟,导致部分玩家下载较旧的配置文件。另外部分中小运营商出于成本考虑,会缓存旧文件。在以往的项目中,出现这种情况时,一般会联系玩家定位问题,如果发现是运营商问题,就会上报给运维人员,网络部的同事会催促运营商进行更改,效率不高。为了降低这种情况发生的可能,我们使用了双 CDN 策略。
具体做法是,对同名文件增加版本号机制,更新文件时将文件内部存储版本号加1,在两个不同的CDN上进行更新,客户端下载时会下载两份文件,以版本号较大的为准。这样,更新配置文件时,只需要同步两个不同CDN中的一个,可以减少CDN更新延迟,降低运营商缓存出现问题的概率。
3. 性能优化
与一般游戏不同,微信小游戏平台本身的 js 脚本执行效率相对较弱。iOS 环境小游戏的 javascript 引擎目前采用的是 JavaScriptCore,默认不开启 jit 优化,js 执行速度比手机端 Safari 慢,从简单测试结果来看,速度大概慢了一倍。从 Profiler 来看,js 脚本执行时间占比大概在 80% 左右。因此,减少脚本的计算量也是性能优化的重要方面。
4 优化
为了解决这些问题,项目组做了以下优化
绘制调用
渲染批次合并和大部分游戏项目类似,需要合理规划同级GameObj使用的图片资源的图集、拼图的使用。
可分为地图背景层、地表、地图物体、坦克、子弹、特效、UI等谜题。尽量保证同一层级的游戏物体使用同一张图集,相邻的精灵使用相同的材质。
面具
游戏中玩家的头像显示为圆形,而微信平台下载的头像为矩形。原有的头像显示采用 Cocos 的 mask 组件进行渲染,效率较低。我们自己实现了一个基于 mesh 的控件,将圆形划分为 n 个三角形,并给这些三角形的顶点分配相应的 UV,从而绘制出圆形的头像。这样可以减少头像渲染时的 batch 开销。
影响检查
Cocos Creator 自带的碰撞系统效率不高,没有做空间划分,不适合大量单位的碰撞检测。另外碰撞体的碰撞盒需要每帧更新。我们的游戏地图中存在大量静态物体(例如地图中的砖块、主基地、钢板等),当玩家在场景中移动时,通过移动摄像机,地图视野会发生变化,因此地图上大量静态物体的世界坐标保持不变,它们的碰撞盒只需要计算一次。
为了解决这个问题,我们给 Cocos 节点增加了静态属性,静态节点的计算结果可以被缓存,避免重复计算。
对象池
游戏中的坦克、子弹、砖块等都使用了对象池,在进入战斗场景时会预先加载足够数量的对象,并在战斗过程中重复使用,以避免实时创建和销毁对象。
避免场景和节点更新
分析 Cocos Creator 源码后我们发现,当一个节点变为活跃状态时,会触发场景的递归遍历,这个开销非常大。
为了避免这种开销,游戏中物体死亡时,不是将其从场景中移除或禁用,而是通过将坐标移到较远的地方设置为死亡状态,代码中不执行相应的逻辑处理。尽量保持帧率稳定,避免性能曲线出现毛刺
裁剪
当物体超出主角的视野范围,且不是持续的特效或声音时,可以将其切断。
模型适配
美术资源分为高、中、低三个等级,策划在资源表中配置不同等级的资源名称,游戏过程中根据机器型号和实际表现选择显示某个等级。
图中横轴为时间(单位为秒),纵轴为FPS游戏开发,可以看出FPS有了明显的提升,通过一系列的优化措施,最终保证了低端的iPhone 5S也能基本满足游戏的需求。
以上就是《欢乐坦克大战》微信游戏的开发总结,感兴趣的朋友可以过来一起交流一下~