0054062nuu0siri7jjquge.png 车库
005407ht1l4z7caklf1asg.png 高配画面
005408l225l84p1d5g1555.png 低配画面
端游经验用于反外挂,安全问题早作预案

  相比端游而言手游面临的网络环境更严峻,我们不可能做到实时通过服务器校验玩家的每一步操作,从延迟,稳定性,流量上讲都不允许。单局的玩法逻辑大都是在客户端完成在结算时才统一上报服务器的,玩家就有可能修改一些关键数据,比方说分数,金币,Buf持续时间等等,从而获得非法收益,这就必然牵扯到令人头疼的反外挂问题。

  基于以前端游的积累我们初期就预想到了这个情况,比较早地开始了对抗准备。我们的外挂对抗体系大体分为三层,第一层是客户端的防御,除了必不可少的协议加密之外,客户端对内存中的关键数据也都是密文存储的并且加上了校验码,这样通过烧饼之类的通用内存修改工具就比较难定位数据的具体位置了,而且就算真的找到了内存地址,修改之后也会导致校验失败,这样客户端就能侦测到内存数据的非法篡改。第二层是我们自己服务器的即时对抗,客户端在单局结算时上报的数据中不光有最终的结果还有一些和结果有关联的中间数据,比方说实际生成了多少金币,是否有出现某种特殊车等等,如果外挂只是修改了部份数据,服务器就可以根据校验公式检测出数据被非法篡改过了,服务端kenny以前在QQ飞车就做过类似的对抗,经验非常丰富。最后一层是互娱安全组的后校验,他们采用的检测方法和我们服务器的类似,只是不会对作弊行为进行实时处罚。

  还有一个要特别注意的是,在Android平台上,Unity的C#脚本是以JIT方式运行的,apk包里的程序集dll文件很容易被Reflector等工具反编译,一旦被别有用心的人知道了客户端逻辑到底如何运作的,就可能做出一些比较逆天的外挂来。当时我们想了很多办法,一开始是做混淆,但发现执行起来不是很方便,对开发存在一定的限制。后来我们想到一个方法,我们可以对程序集dll文件进行加密,这样通用的反编译工具就打不开了,但苦于我们没有 Unity及其修改过的mono组件的源代码, 也不擅长逆向工程,我们把这个需求提给Unity的开发商,可能出于某些原因,他们也没有太积极的反馈,后来还是安全组给了我们支持,他们通过逆向工程实现了对程序集文件的加解密,之后公司开发/代理的Unity手游应该都有采用这个加密方案。

005408drehj2ckz2qh2cdi.png
崩溃上报灵活处理是解决之道

  针对移动应用,MIG研发了一套叫做RQD的崩溃上报系统,MSDK对它作了统一接入,所以凡是接入了MSDK组件的移动端游戏在客户端发生崩溃时都会自动捕捉上报异常的现场信息,对分析崩溃来说最有用的可能就莫过于调用栈了。

  RQD 虽然很强大,不光支持C/C++,ObjC这些原生语言的调用栈的还原,也支持Java的调用栈还原,但可惜的是Unity开发的游戏大多的异常其实发生在C#层,iOS平台还好,C#是以AOT方式预先编译成了原生代码,调用栈会被当作C语言的对待,从函数名上也能基本定位到C#中具体发生崩溃的地方。但Android平台就没这么幸运了,C#是被预先编译为IL中间代码,以JIT模式在mono虚拟机中运行的,所以一旦C#层出现异常,RQD报上来的调用栈反映的都是虚拟机自身发生了异常,无法定位异常发生的具体位置,然而Android崩溃率大大高于iOS,恰恰是需要更多关注的。

  后来通过猜测和试验,我们发现其实在C#层产生异常时,Unity可以通过回调的方式把异常类型和调用栈告知应用,这正是我们需要的,但如何把这些信息上报又成了问题,因为一旦RQD捕捉到异常就会立即kill掉应用,可能应用都没有机会处理这些信息,我们把情况反馈给了RQD的研发组,但短时间内他们也很难有比较完善而系统的解决方案。后来通过和RQD开发同事不断的探讨终于找到了一种看起来简陋但却行之有效的方案:由他们提供一个发生异常时延迟kill 掉进程的定制版本,在异常发生时我们把需要额外上报的异常信息以追加的方式写入会上报的tomb文件。采用这种方法,我们所需的C#异常信息在 Android平台也能有效地通过RQD上报了,而且能在后台页面比较方便的查看。

005409zgltmxmxqxm9q519.png
引擎选择必须要适合自身项目,能否驾驭和掌控至关重要

  对于新的手游项目来说,从一开始立项就需要考虑好,选引擎就是要适合自己的,看你的游戏类型,团队规模,还有很重要的一点是看周围环境是怎么样的,如果你选择了一款很少人用的引擎,当你需要交流、需要别人帮助的时候,你可能都找不到人。国人做事讲究天时地利人和,我觉得有一个好的交流分享环境应该算是地利之一吧。

  当时我们选择Unity来开发也是有多方面考虑的。首先我们要开发的是一款中小型的,3D的,移动端游戏,需要同时发布到iOS和Android两个平台,我们的团队规模很小,Unity的应用非常广泛,有不少和我们同类型同规模的游戏的成功案例,比如一起车车车,神庙逃亡等,公司也已经有了Unity开发的且已上线的移动端游戏项目,其次Unity本身也是一款开发效率很高的引擎,它的集成开发环境非常好用,资源整合很方便,游戏逻辑主要用C#等托管语言编写,和引擎本身的实现是完全隔离开的,开发,编译效率都非常的高,改完代码,几秒钟就能看到实际跑起来的效果,手游这种特别需要快速迭代的项目再适合不过了。再次,对于3D游戏开发开说,Unity提供的功能也是相当完备的,扩展起来也很方便,可以用C/C++,ObjC,Java编写运算密集型的或者需要直接访问系统底层接口的组件以插件的形式供游戏逻辑层访问,编辑器功能也很容易扩展,此外Unity的AssetStore提供了一个很好的组件交流平台,需要什么额外功能的时候去看看,也许能避免重复发明轮子。

  2D 游戏我们目前不推荐Unity来开发,毕竟这个引擎原本是面向3D游戏的,功能也主要是围绕这一块来做的,如果你只是做一个2D游戏,它不可能把你不需要的功能全部都干净的拿掉,肯定会有很多额外的开销。虽然Unity最近的新版本也逐渐在增强对2D游戏的支持,但可能还不太完善,项目案例应该也少,2D 的话我们还是推荐使用公司的自研引擎或者是应用广泛的cocos2D。

  对于已上线的产品来说,在游戏玩法不断扩展,新资源不断增加的运营过程中,是否能够把消耗的增加也控制在可接受范围之内,这是很关键的。比方说,Unity引擎比较大的一个问题就是内存控制不是那么的好,不太注意的话很可能让你的内存占用超标。

  一方面你自己的程序框架要合理,另一方面就取决于你对引擎的掌握程度了,如果你都不太清楚究竟是什么地方吃掉了CPU时间,什么地方占掉了内存,那可能遇到问题时你就束手无策了,这其实是一个失控的状态,对于一个已上线产品来说是很可怕的。我们不怕问题有多大,最怕的是虽然知道遇到了问题,却搞不清楚问题的具体缘由,更谈不上彻底去解决它了。Unity是一个比较封闭的引擎,少有机会去研究和修改它的源代码,所以对它的掌控,我们目前做的也并不算特别好,现在我们也是在不断的加强,看怎么去彻底的驾驭它。


  作者:胡波,人物介绍:2003年开始接触游戏编程,2006年进入游戏行业,2010年加入腾讯,先后参与多款UE3次世代PC端网络游戏的开发和预研,2013年初开始移动端3D网游的研发,见证了天天飞车从萌芽到诞生及成长的整个过程,现为客户端研发负责人。
锐亚教育

锐亚教育,游戏开发论坛|游戏制作人|游戏策划|游戏开发|独立游戏|游戏产业|游戏研发|游戏运营| unity|unity3d|unity3d官网|unity3d 教程|金融帝国3|8k8k8k|mcafee8.5i|游戏蛮牛|蛮牛 unity|蛮牛