帧同步游戏的计划

《腾讯桌球:客户端总》

自从单机游戏到网络游戏

单机游戏,这里指即时的动作类游戏,玩家输入操作,通过极端运算而开展的玩。加入了差不多人口网络下,玩家的输入不仅仅只是在当地的巅峰上运算,还会见通过网合,使多总人口足以跟一个虚拟环境中而玩。由此,网络多人口争先节奏的动作游戏带来了新的问题:一致性响应性带宽延迟。网络游戏的实时PVP就是以平衡马上四沾之要素。

本次分享总结,起源于腾讯桌球项目,但是不仅仅限于项目本身。虽然基于Unity3D,很多东西一律适用于Cocos。本文自以下10雅点进展阐释:架构设计、原生插件/平台相互、版本及补丁、用剧本,还是不要?这是一个题目、资源管理、性能优化、异常以及Crash、适配与配合、调试和开发工具、项目运营。

帧同步的引入

帧同步应该是引入多口网络之后,能想到最直接
的一起方式,在优质状态下,是方便的解决方案。而现实是,帧同步需要发送大量的轴数据来使游戏逻辑,需要客户端能在一段时间保持网络稳定(低顺延,少量的网抖动)。此外,还有状态并技术,帧同步是将玩家的动作直接同步到其它玩家的顶峰上,通过明确的运算达到共同效果,而状态并是富有客户端的动作发送到服务器,由服务器计算并将最终状态广播下发。网上发大量底资料对当时有限种植技术进行比,下面就对帧同步的艺做讲述。

1.架构设计

吓的架使常见项目之多口团队开发及代码管理,也用寻错误和季维护。

  • 框架的精选:需要依据集团、项目来进展选择,没有最好之框架,只有极适合的框架。
  • 框架的应用:统一之框架能正式大家的行事,互相之间可于平切换,可维护性大大提升。除此之外,还会代码解耦。例如StrangeIOC是一个超轻量级和惊人可扩大的主宰反转(IoC)框架,专门为C#同Unity编写。已知道公司内部采用StrangeIOC框架的游玩有:腾讯桌球愉快麻将植物大战僵尸Online。<https://github.com/strangeioc/strangeioc>

仗注入(Dependency
Injection,简称DI),是一个着重的面向对象编程的规律来减少计算机程序的耦合问题。依赖注入还有一个名称为控制反转(Inversion
of
Control,英文缩写为IoC)。依赖注入是这样一个进程:由于某客户类只靠让服务类的一个接口,而未因让实际服务类,所以客户类才定义一个注入点。在程序运行过程中,客户类非直实例化具体服务类实例,而是客户类的运转上下文环境专程组件担负实例化服务类,然后以那注入及客户类中,保证客户类的正规运作。即对象在给创造的早晚,由一个运转上下文环境要专门组件将那所据的服务类对象的援传递给它们。也得以说,依赖让注入到目标吃。据此,控制反转是,关于一个靶如何取得他所指的对象的援,这个责之反转

StrangeIOC采用MVCS(数据模型 Model,展示视图 View,逻辑控制
Controller,服务Service)结构,通过信息/信号进行互动与通信。整个MVCS框架跟flash的robotlegs基本一致,(忽略语言不同等)详细的参照<http://www.cnblogs.com/skynet/archive/2012/03/21/2410042.html>。

  • 数据模型 Model:主要担负数据的贮存和着力数据处理
  • 显视图 View:主要担负UI界面展示暨动画片表现的拍卖
  • 逻辑控制 Controller:主要担负作业逻辑处理,
  • 劳动Service:主要担负独立的网络收发请求等的一些力量。
  • 消息/信号:通过信息/信号去解耦Model、View、Controller、Service这四种植模块,他们中通过信息/信号进行相互。
  • 绑定器Binder:负责绑定消息处理、接口和实例对象、View与Mediator的相应关系。
  • MVCS
    Context:可以解呢MVC各个模块存在的上下文,负责MVC绑定和实例的创工作。

腾讯桌球客户端项目框架

帧同步的法则

了解帧同步技术,不妨可以参照下DOOM/QUAKE I/II/III
网络型的嬗变,约翰·卡马克也FPS的纱合写下了原型,后面的版又以当下基础及做出了多改善版。

帧同步技术最好着重之基本功概念:
一致的输入+同等之时=同等之来得
意思是每个客户端接受之输入是相同的,执行的逻辑帧也是同的,那么结果为是同步一致的。为了跟每个机器执行的快无关,每个逻辑帧为一定帧数固定时长,游戏中的实体都是依这种设定运算,因此活动、碰撞等还能够算是有一致之结果。而渲染帧(一般为30顶60帧),则是根据逻辑帧(10届20幅)去插值,得到一个“平滑”的示。逻辑帧实际是凡由于一个个定时发的“网络帧”来使,而渲染帧则由地方CPU的update驱动,渲染帧只是逻辑帧无限逼近(插值),如果逻辑帧突然顿,则打就会见卡壳在那么一帧状态,这即是lockstep的由来。

为保险游戏联合实施的一致性,代码必须按lockstep的方法组织运算,不因地方客户端的帧率,时间要随机数。理想状态下,每个“网络帧”被立马吸纳,客户端渲染帧都能够满帧运算,游戏就是比如播放影片一样。但当网络游戏中,各个客户端的硬件及网络状态都非一样,可能会见造成客户端收到“过去时光”里之一律堆积网络帧,因此,必须要发处理这些堆积起来的网络数据的力量。最简便易行的做法是加速播放(快进),根据堆积的量计产生加快比率,以此快捷地推行逻辑帧,尽快地追上最新的实时帧。同时,在增速的长河中,可以设想丢弃用户之操作,因为玩家看来的凡“过去”的状态,此时开展控制打击是从来不意义的。另外一种植处理方式是,直接运算直至追上时的网络帧,这样会一直闪现到“最新”的状态,玩家可以立刻操作。比较建议采用快进的招数,这种方式可为玩家感受及对立自然的娱乐画面。这无异基础特性也用于支持后的断线重连。

1.2代码目录的团:相似客户端用得比多之MVC框架,怎么划分目录?

预先照工作功能划分,再遵照MVC来分。”蛋糕心语”就是应用的这种办法。
事先以MVC划分,再比如业务功能划分。”D9″、”宝宝斗场”、”魔法花园”、”腾讯桌球”、”欢乐麻将”使用的这种方式。

依据使用习惯,可以自行选择。个人推举”先照工作职能划分,再按 MVC
来划分”,使得模块更聚焦(高内聚),第二种植艺术因此几近矣意识随着项目之营业模块增多,没有第一栽那么好保安。
Unity项目目录的团队:结合Unity规定的部分异常的用处的文书夹,我们建议Unity项目文件夹组织办法如下。

中间,Plugins支持Plugins/{Platform}这样的命名规范:

  • Plugins/x86
  • Plugins/x86_64
  • Plugins/Android
  • Plugins/iOS

设是Plugins/{Platform},则加载Plugins/{Platform}目录下之文书,否则加载Plugins目录下的,也就是说,如果有{Platform}目录,Plugins根目录下的DLL是不见面加载的。
此外,资源集团使用分文件夹存储”成品资源”及”原料资源”的法门处理:防止无关资源参与打包,RawResource即原始资源,Resource即成品资源。当然并无限于RawResource这种样式,其他Unity规定之非正规文件夹都好这么,例如Raw
Standard Assets。

帧同步的响应性

对实时PVP的戏吧,手感流畅的角色决定体验好重点。玩家的输入往往在几十分之一秒内,就开始显得变化,而于帧同步中,玩家的输入发送至网络,下一个网络帧操作回来时争先处理并出示
,当网络未安定时,常常会时快时慢,非常难以预测输入动作后,角色会在什么时候打影响。要解决是题材,可以参见下传语音业务的做法,在接网络数据时,不立即处理,而是被有的操作多一个定点的推,在缓的流年内尽量收集更多之网络包,然后按照一定的岁月去播放(运算)。这种做法相当给立了一个网缓冲区,平滑了因为网络抖动而时快时慢的数据包。这里丰富的固化延迟可以遵循玩家所当的网络延迟来设置,可以动态地得一个连接稳定(避免抖动)的值,可以采取累计要抽样的加权平均来博延迟。玩家发出之操作而在固化延迟外收,游戏就足以顺理成章运行,网络的振动已经给间隔等的逻辑帧抹平了。

1.2企业组件

  • msdk(sns、支付midas、推送灯塔、监控Bugly)
  • apollo
  • apollo voice
  • xlua

手上我们的腾讯桌球、四皇家军棋都联网了apollo,但是要服务器不使用apollo框架,不建议客户端接apollo,而是径直接msdk减少二次封装信息的掉和拉动的失实,方便以后升迁维护,并且减少导入无用的代码。

TCP还是UDP,这是一个题材

保了逻辑运算的一致性和逻辑帧的平加速,基本上可以入手将一个单机游戏改成为多丁联手游戏了,在局域网运作勉强还能够经受,可惜,我们开的是互联网及之玩。接下来了解一下动网的缓情况。首先是TCP,保证了包序,丢包活动重传,看起便是咱们怀念如果的拼图,并且一度有人帮助咱兑现了,而且还拉扯老板省了一大笔钱。诚然,可靠的传输协议非常诱人,并且为产生无数得逞的例证,且重新权衡下缺点还举行决定。TCP是根据重新传来保证可靠性,如果IP包丢包,TCP协议要等待至少2单来回时延才会再次发送这个数据包,丢包重甚至会见断线,一旦断线,则触发断线重新连流程。看看下面一组数。

图片 1

Paste_Image.png

当即是腾讯一缓以忍者格斗为问题的ACT手游为出的数,可以视于各种网络状态下,UDP的呈现(延迟布)基本上都优于TCP。

那到UDP,非面向连接的导协议,没有电动重传,没有死控制,不克保证包序,甚至不可知确保可至,只保证了数量报完的功底特性。优点是缓小、数据传效率高、资源开发小,如果因此来作为网络游戏的导方案,需要在应用层定制更多适用于网游的特点。在UDP基础及定制一个应用层的磋商,难度比较大。基于UDP也发出一个通用的解决方案UDT,保证了可靠性与包序,但是跟TCP类似的,UDT也是根据超时重传的法门确保保险。下面我怀念拿有专用定制的方案将出来讨论。

率先,帧同步需要每帧广播数据,广播的频率十分高,这要求每次广播的数量而足够小,最好每个网络帧在一个MTU以下,这样好避在IP层分片,降低延迟,互联网的MTU标准也576字节,有效载荷长度控制在(576-8-20)548字节以内。为了尽量避免重传,游戏里可以为此冗余的艺术——每个帧数据包实际包含了千古2帧之数量,也就是是历次发3帧的数来对抗丢包。三独保险里面如出一个管没扔,就不影响游戏。另外,定制的方案还待出一个告“下充斥”丢失帧的特征,以防止连续3个包都弃的场面。对于“下充斥”特性,则足以考虑用TCP。这是咸冗余的做法,缺点是会见造成流量增加2加倍。还有同种动态冗余算法,根据客户端的丢包状况动态调整冗余倍数,上面介绍的那款ACT游戏就是为此了这种方式,本质上或者用流量换速度。

接发包速率对一款PVP竞技型的商业娱乐吧任重而道远,目前尚只是是上学到皮毛,以后深入摸底后再补偿。除此之外,后续还需要服务器介入,解决断线重连与反作弊等问题,先勾勒到此。

1.3叔正插件选型

  • NGUI
  • DoTween
  • GIF
  • GAF
  • VectrosityScripts
  • PoolManager
  • Mad Level Manger

2.原生插件/平台相互

则多时用Unity3D进行娱乐支付时,只需要使用C#进展逻辑编写。但奇迹不可避免的消运用以及编排原生插件,例如有些叔着插件才供C/C++原生插件、复用已有的C/C++模块等。有部分作用是Unity3D实现非了,必须要调用Android/iOS原生接口,比如取手机的硬件信息(UnityEngine.SystemInfo没有提供的片)、调用系统的原生弹窗、手机激动等等

2.1C/C++插件

修和运原生插件的几乎独重点点:

  • 始建C/C++原生插件
    • 导出接口必须是C ABI-compatible函数
    • 函数调用约定
  • 在C#中标识C/C++的函数并调用
    • 标识 DLL 中之函数。至少指定函数的名号和带有该函数的 DLL 的称号。
    • 始建用于容纳 DLL
      函数的切近。可以使用现有类,为各个一样非托管函数创建单独的接近,或者创造包含一组有关的非托管函数的一个类。
    • 每当托管代码中创造原型。使用DllImportAttribute标识 DLL
      和函数。 用staticextern修饰符标记方法。
    • 调用 DLL 函数。像处理外任何托管方一致调用托管类上之章程。
  • 在C#被创造回调函数,C/C++调用C#回调函数
    • 创托管回调函数。
    • 始建一个委托,并拿那个当做参数传递给
      C/C++函数。平台调用会自动将委托转换为普遍的回调格式。
    • 担保以回调函数完成其行事前,垃圾回收器不会见回收委托。

那么C#跟原生插件之间是怎样落实互动调用的也?在打明白此问题之前,我们事先押下C#代码(.NET上之程序)的施行的长河:(更详尽一点之牵线好参见我之前写的博客:http://www.cnblogs.com/skynet/archive/2010/05/17/1737028.html)

  • 用源码编译为托管模块;
  • 拿托管模块组成也序集;
  • 加载公共语言运行时CLR;
  • 推行顺序集代码。
    注:CLR(公共语言运行时,Common Language
    Runtime)
    以及Java虚拟机一样也是一个运行时环境,它担负资源管理(内存分配与废品收集),并保管应用和底部操作系统之间必要之分开。
    为增强平台的可靠性,以及以上面向事务的电子商务应用所求的安居乐业级别,CLR还要背其他部分职责,比如监视程序的运作。按照.NET的说法,在CLR监视之下运行的次第属于“托管”(managed)代码,而非以CLR之下、直接当裸机上运行的使或零部件属于“非托管”(unmanaged)的代码

即时几乎单过程自己总也产图:

希冀 .NET上的程序运行

掉调函数是托管代码C#惨遭的定义的函数,对回调函数的调用,实现从非托管C/C++代码中调用托管C#代码。那么C/C++是如何调用C#的吗?大致分为2步,可以为此生图表示:

  • 将回调函数指针注册到非托管C/C++代码中(C#受回调函数指委托delegate)
  • 调用注册过的托管C#函数指针

相较托管调用非托管,回调函数方式有些复杂一些。拨调函数非常适合重复执行的天职、异步调用等景象下以
是因为点的牵线好理解CLR提供了C#程序运行的条件,与非托管代码的C/C++交互调用也出于她来形成。CLR提供零星栽用于和非托管C/C++代码进行互的机制:

  • 阳台调用(Platform
    Invoke,简称PInvoke或者P/Invoke),它一旦托管代码能够调用从非托管DLL中导出的函数。
  • COM 互操作,它使托管代码能够通过接口与组件对象模型 (COM)
    对象交互
    。考虑超过平台性,Unity3D不使这种办法。

阳台调用依赖让头版数据以运作时找导出的函数并封送(Marshal)其参数。
下图展示了立同一经过。

注意:

  1. 除外关系回调函数时以外,平台调用方法调用从托管代码流向非托管代码,而并非会因反方向流动。
    虽然平台调用的调用只能由托管代码流向非托管代码,但是数量还是可看做输入参数或输出参数在片单趋势流动
  2. 希冀中DLL表示动态库,Windows平台指.dll文件、Linux/Android指.so文件、Mac
    OS
    X指.dylib/framework文件、iOS中只能使用.a。后文都使用DLL代指,并且DLL使用C/C++编写

当”平台调用”调用非托管函数时,它用逐一执行以下操作:

  • 追寻包含该函数的DLL。
  • 拿该DLL加载到内存中。
    搜函数在内存中之地址并拿那参数推到堆栈上,以封闭送所用的数码(参数)。

注意
就于首先浅调用函数时,才见面寻找和加载 DLL
并摸索函数在内存中的地方。iOS中采用的是.a已经静态打包到最后实施文书被。

  • 拿控制权转移给非托管函数。

2.2Android插件

Java同提供了这么一个扩张机制JNI(Java Native
Interface),能够跟C/C++互相通信。

注:

  • JNI
    wiki-https://en.wikipedia.org/wiki/Java\_Native\_Interface,这里不透介绍JNI,有趣味的好自动去研究。如果您还未掌握JNI也不用怕,就比如Unity3D使用C/C++库一样,用起还是比较简单的,只需要明白者东西即可。并且Unity3D对C/C++桥接器这块做了包装,提供AndroidJNI/AndroidJNIHelper/AndroidJavaObject/AndroidJavaClass/AndroidJavaProxy方便使用等,具体运用后在介绍。JNI提供了多的API实现了Java和另语言的通信(主要是C&C++)。从Java1.1始,JNI标准成为java平台的等同有些,它同意Java代码和其它语言描绘的代码进行互动,保证本地代码能工作在外Java
    虚拟机环境下。”
  • 用作文化扩展,提一下Android
    Java虚拟机。Android的Java虚拟机有2单,最开头是Dalvik,后面Google在Android
    4.4系新增同种植使运行模式ART。ART与Dalvik
    之间的要害区别是那个具有超前 (AOT) 编译模式。 根据 AOT
    概念,设备安装使用时,DEX 字节代码转换仅进行相同不良。 相比叫
    Dalvik,这样可实现真正的优势 ,因为 Dalvik 的即时 (JIT)
    编译方法要以历次运行应用时还进行代码转换
    。下文中因故Java虚拟机代指Dalvik/ART。

C#/Java都得同C/C++通信,那么通过编制一个C/C++模块作为桥接,就让C#以及Java通信成为了也许,如下图所示:

横流:C/C++桥接器本身和Unity3D没有直接涉及,不属于Android以及Unity3D,图备受位居Unity3D中凡是为代指libunity.so中实现的桥接器以象征真实的状态。

透过JNI既可以用来Java代码调用C/C++代码,也不过用来C/C++代码与Java(Dalvik/ART虚拟机)的并行。JNI定义了2单关键概念/结构:JavaVMJNIENVJavaVM提供虚拟机创建、销毁等操作,Java中一个历程可以创造多单虚拟机,但是Android一个进程只能有一个虚拟机JNIENV大凡线程相关的,对应的凡JavaVM中之目前线程的JNI环境,只有附加(attach)到JavaVM的线程才产生JNIENV指针,通过JNIEVN指针可以得到JNI功能,否则不能够调用JNI函数。

C/C++要访问的Java代码,必须使力所能及访问到Java虚拟机,获取虚拟机有2蒙方法:

  • 以加载动态链接库的早晚,JVM会调用*JNI_OnLoad(JavaVM jvm, void
    reserved)\
    *,第一独参数会传播JavaVM指针。
  • 在C/C++中调用JNI_CreateJavaVM(&jvm, (void**)&env,
    &vm_args)创建JavaVM指针。

所以,咱只是需要在编辑C/C++桥接器so的上定义****JNI_OnLoad(JavaVM
jvm, void

reserved)方法即可,然后将JavaVM指针保存起来作为上下文使用

获得到JavaVM之后,还无能够一直将到JNI函数去获取Java代码,必须通过线程关联的JNIENV指针去取。所以,作为一个吓的出习惯于历次取一个线程的JNI相关功能时,先调用AttachCurrentThread();又要每次经过JavaVM指针获取当前之JNIENV:java_vm->GetEnv((void)&jni_env,
version),一定是已附加到JavaVM的线程**。通过JNIENV可以得到到Java的代码,例如你想当当地代码中做客一个靶的字段(field),你可以像下这样做:

  • 对于类,使用jni_env->FindClass获接近对象的援
  • 对字段,使用jni_env->GetFieldId抱字段ID
  • 用相应的法(例如jni_env->GetIntField)获取字段的价值

看似地,要调用一个方,你step1.得得一个类对象的援obj,step2.凡是艺术methodID。这些ID通常是因于运行时中数据结构。查找到它们需几字符串比较,但要你其实去实施其赢得字段或者开方法调用是可怜急匆匆之。step3.调用jni_env->CallVoidMethodV(obj,
methodID, args)

自上面的以身作则代码,我们得以望使用旧的JNI方式去跟Android(Java)插件交互是基本上的繁琐,要和谐做最多之事务,并且以性需要团结着想缓存查询到之办法ID,字段ID等等。幸运的凡,Unity3D已经为我们封装好了这些,并且考虑了性能优化。Unity3D主要提供了瞬间2个级别的包来帮衬高效编写代码:

注:Unity3D中对应的C/C++桥接器包含在libunity.so中。

  • Level
    1:AndroidJNI、AndroidJNIHelper,原始之包裹相当给我们地方自己修的C#
    Wrapper。AndroidJNIHelper
    和AndroidJNI自动完成了成百上千任务(指找到类定义,构造方法等),并且动用缓存使调用java速度又快。AndroidJavaObject和AndroidJavaClass基于AndroidJNIHelper
    和AndroidJNI创建,但于处理自动就有为起成百上千友好的逻辑,这些近似为有静态的本,用来做客java类的静态成员。更详实接口参考帮助文档:http://docs.unity3d.com/ScriptReference/AndroidJNI.html,http://docs.unity3d.com/ScriptReference/AndroidJNIHelper.html
  • Level
    2:AndroidJavaObject、AndroidJavaClass、AndroidJavaProxy,这个3独八九不离十是根据Level1的卷入提供了又胜层级的包使用起来再次简便易行,这个于第三片段缕介绍。

2.3iOS插件

iOS编写插件比Android要简明好多,为Objective-C也是
C-compatible的,完全匹配标准C语言。
这些就是可非常简单的保管一交汇 extern
“c”{},用C语言封装调用iOS功能,暴露被Unity3D调用。并且可以跟原生C/C++库一样编成.a插件。C#暨iOS(Objective-C)通信的原理跟C/C++完全平等:

而外,Unity
iOS支持插件自动集成方式。所有在Asset/Plugings/iOS文件夹着后缀名为.m ,
.mm , .c ,
.cpp的文书都用活动并入到已经生成的Xcode项目蒙。然而,最终编进执行文书被。后缀为.h的文件不可知为含有在Xcode的类型树中,但他俩拿起在对象文件系统中,从而使.m/.mm/.c/.cpp文件编译。这样编写iOS插件,除了用针对iOS
Objective-C有肯定了解外面,与C/C++插件没有区别,反而更简明。

3.版及补丁

另游戏(端游、手游)都该提供娱乐内更新的途径。一般娱乐分为全量更新/整包更新、增量更新、资源创新。

  • 全量
    android游戏内完全安装包下载(ios跳反到AppStore下载)
  • 增量:主要指android省流量更新
    • 得以bsdiff生成patch包
    • 应用宝也供增量更新sdk可供应对接
  • 资源
    Unity3D通过应用AssetBundle即可兑现动态更新资源的效能。

手游在贯彻这块时要留意的几乎点:

  • 打发布有得要是提供娱乐内更新的路子。即使是删掉测试,保不准这中需要展开资源要BUG修复更新。很多玩家并不知道如何创新,而且Android手机应用分发平台多样,分发平台自也未见面和官方并更新(特别是有些之分发平台)。
  • 更新功能而提供强制更新、非强制更新配置化选项,并点名哪些版本可以不高更,哪些版本要强还。
  • 当游戏供非强制更新功能下,现网一定会在多独版。如果欲对不同版本做不同的更新,例如配置文件A针对1.0.0.1改动了一致项,针对1.0.0.2改了别一样件,2个本子要各自更新对应的改,需要协调实现创新策略IIPS不提供者职能。当用复杂的换代策略,推荐自己编排更新服务器和客户端逻辑,不动iips组件(其实自己实现啊蛮粗略)。

并未营业更的人口会见挑选二进制,认为二进制安全、更粗,这对准端游/手游外网只设有一个本子的戏可,对一般不愈升版的手游并无适合,反而会针对创新和保护带来非常老之麻烦。

  • 部署利用XML或者JSON等文本格式,更便民多本的匹配和翻新。最开头腾讯桌球客户端应用的老二迈入制格式(由excel转换而来),但是趁运营配置格式需要充实字段,这样一直版本程序即使解析不了初的二进制数据,给兼容和更新带来了生特别之难为。这样就算要求地方提到的对准非齐做不同更新,又要安排一始就是留下好足够的扩大项,其实不管怎么预留扩展为要命麻烦及达到要求的成形,而且同开始见面把配置表复杂化但是实际只有出同张或几摆放才见面转结构。
  • iOS版本的送审版本用连接一定的含新情节之服务器,现网服务器还无带有新情节。送审通过下,上架游戏现网服务器会进行更新,iOS版本要连续现网服务器如果不送委服务器,但是这中又无能够修改客户度,这个切换需经过服务器发开关进行控制。例如通过点名送审的iOS游戏版本号,客户端判断当地版本号是否也送审版本,如果是连连送审服务器,否则连现网服务器。

4.用剧本,还是不要?这是一个问题

有利于更新,减少Crash(特别是使C++的cocos引擎)

经者一样节【版本和补丁】喻要兑现代码更新是好窘迫的,正式这个缘故客户端支出的下压力是比异常的,如果起了比较严重的BUG必须作强制更新版本,使用脚本可以缓解者问题。
鉴于Unity3D手游更新资金比异常,而且目前腾讯桌球要求无可知强制更新,这致使新本子的活动覆盖率提升于缓慢、出现问题以后难以修复。针对这状态,考虑引入lua进行活动出,后续发布活动以及修复bug只待发布lua资源,进行资源创新即可,大大降低了宣布与修复问题之资产。
可选方案还有使用Html5进展活动出,目前打被曾经先期埋了Html5移动入口,并且一度为此来发了”玩家调查”、”腾讯棋牌宣传”等。但是跟lua对比,不可知不负众望和Unity3D的吃水融合,体验不如用lua,例如非克操作游戏中的ui、不可知就复杂界面的制造、不克复用已有些职能、玩家付费充值以及已有的吧会见出反差

打闹脚论之君——Lua

当信用社中魔方比较好用lua,火隐忍者(手游)unity+ulua,全民水浒cocos2d-x+lua等等都产生应用lua进行付出。我们得以利用企业里的xlua组件,也得采用ulua<[http://ulua.org/\]>、UniLua<[https://github.com/xebecnan/UniLua\]>等等。

5.资源管理

5.1资源管理器

  • 工作并非直接用引擎或系统原生接口,而是包装一个资源管理器负责:资源加载、卸载
  • 兼容Resource.Load与AssetBundle资源彼此变更需要,开发中动用Resource.Load方式若毋庸打AB包效率又胜似
  • 加载资源时,不管是同步加载还是异步加载,最好是以异步编码方式(回调函数或者信息通知机制)。如果哪一样龙资源由地方加载改吗从服务器按需加载,而游戏受之逻辑都是一同方式编码的,改起将坏痛苦。其实异步编码方式很简单,不较同方式复杂。

5.2资源类型

  • 图片/纹理(对性、包体影响最为深因素)
  • 音频
    • 背景音乐,腾讯桌球使用.ogg/.mp3
    • 音效,腾讯桌球使用.wav
  • 数据
    • 文本
    • 二进制
  • 动画/特效

5.3图片-文件格式与纹理格式

  • 常用的图像文件格式来BMP,TGA,JPG,GIF,PNG等;
  • 常用的纹路格式来R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8, A8R8G8B8相当于。

文件格式是图像为存储信息一旦以的针对信息之异编码方式,它存储在磁盘中,或者内存中,但是并无克被GPU所识别,因为坐向量计见长的GPU对于这些纷繁的精打细算无能为力。这些文件格式当让玩读入后,还是用通过CPU解压成R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8,
A8R8G8B8抵像素格式,再传递至GPU端进行应用。
纹理格式是会被GPU所识别的比如说素格式,能叫快速寻址并采样。举个例子,DDS文件是打开发中常用之文件格式,它其中可以涵盖A4R4G4B4的纹理格式,也堪分包A8R8G8B8之纹路格式,甚至足以涵盖DXT1的纹路格式。在这里DDS文件有点容器的代表。OpenGL
ES
2.0支撑上述关联的R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8,A8R8G8B8等纹理格式,其中
R5G6B5,A4R4G4B4,A1R5G5B5每个像素占用2个字节(BYTE),R8G8B8每个像素占用3单字节,A8R8G8B8每个像素占用
4只字节。
基于OpenGL ES的削减纹理有广阔的如下几种实现:

  1. ETC1(Ericsson texture compression),ETC1格式是OpenGL
    ES图形标准的同等部分,并且为有的Android设备所支撑。
  2. PVRTC (PowerVR texture compression),支持的GPU为Imagination
    Technologies的PowerVR SGX系列。
  3. ATITC (ATI texture compression),支持的GPU为Qualcomm的Adreno系列。
  4. S3TC (S3 texture
    compression),也让称为DXTC,在PC上广泛被采取,但是于运动装备上还是属于异常事物。支持之GPU为NVIDIA
    Tegra系列。

5.4资源工具

发矣标准就是可举行工具检查,从源头及包

  • 资源导入检查
  • 资源打包检查

6.性能优化

掉帧主要针对GPU和CPU做分析;内存占用大重点针对美术资源,音效,配置表,缓存等分析;卡顿为需对GPU和CPU峰值分析,另外IO或者GC也容易导致。

6.1工欲善其事,必先利其器

  • Unity Profiler
  • XCode instruments
  • Qualcomm Adreno Profiler
  • NVIDIA PerfHUD ES Tegra

6.2CPU:最佳标准减少计算

  • 复用,UIScrollView Item复用,避免频繁创建销毁对象
  • 缓存,例如Transform
  • 运算裁剪,例如碰撞检测裁剪
    • 简短碰撞检测(划分空间——二分/四叉树/八叉树/网格等,降低碰撞检测的多寡)
    • 确切碰撞检测(检查候选碰撞结果,进而确定目标是不是真实发生碰撞)
    • 休眠机制:避免模拟静止的球体
  • 逻辑帧与渲染帧分离
  • 分帧处理
  • 异步/多线程处理

6.3GPU:最佳条件减少渲染

  • 纹理压缩
  • 批处理减少DrawCall(unity-Static Batching和Dynamic Batching,cocos
    SpriteBatchNode)
  • 压缩无效/不必要绘制:屏幕外的剪裁,Flash脏矩阵算法,
  • LOD/特效分档
  • NGUI动静分离(UIPanel.LateUpdate的淘)
  • 决定角色骨骼数、模型面数/顶点数
  • 降帧,并非所有场景都用60幅(腾讯桌球游戏场景60帧,其他场景30轴;天天酷跑,在初步玩前,FPS被限定为30,游戏开始后FPS才为60。天天飞车的FPS为30,但是当用户一段时间不接触击界面后,FPS自动降)

6.4内存:最佳标准减少内存分配/碎片、及时放出

  • 纹理压缩-Android ETC1、iOS PVRTC 4bpp、windows DXT5
  • 对象池-PoolManager
  • 联合空闲图集
  • UI九宫格
  • 删去不用的脚本(也会占用内存)

6.5IO:最佳标准减少/异步io

  • 资源异步/多线程加载
  • 预加载
  • 文件减少
  • 理所当然规划资源统一打包,并非texturepacker打包成大图集一定好,会大增文件io时间

6.6网:其实为是IO的平栽

动单线程——共用UI线程,通过波/UI循环驱动;还是多线程——单独的网线程?

  • 单线程:由游戏循环(事件)驱动,单线程模式比较使用多线程模式开发、维护简单很多,但是性能比多线程要差有,所以于网IO的下,需要留意别阻塞到耍循环。说明,如果网络IO不复杂的状态下,推荐应用该模式。
    • 当UI线程中,别调用可能过不去的网络函数,优先考虑非阻塞IO
    • 马上是网开发者经常犯之错有。比如:做一个简单易行而
      gethostbyname()
      的调用,这个操作以稍范围被无会见存在其他问题,但是当小情况屡遭具体世界之玩家也会因此阻塞数分钟的久!如果您于
      GUI 线程中调用这样一个函数,对于用户来说,在函数阻塞时,GUI
      一直还处在 frozen 或者 hanged
      状态,这自用户体验的角度是绝不同意的。
  • 大多线程:单独的大网线程,使用独立的网络线程有一个良鲜明的益处,主线程可以将水污染活、累活交给网络线程做使UI更通畅,例如消息之编解码、加解密工作,这些还是死耗时的。但是采取多线程,给支付和保护带来一定财力,并且使没有早晚之涉写出来的纱库不那么平稳,容易失误,甚至招致游戏崩溃。下面是几接触注意事项:
    • 断千万别当网线程中,回调主线程(UI线程)的回调函数。而是网络线程将数据准备好,让主线程主动去抱,亦要说网络线程将网络数据作一个事件驱动主线程去取。当年本人于于是Cocos2d-x +
      Lua做魔法花园的手机demo时,就用的多线程模式,最初于网络线程直接调用主线程回调函数,经常会面造成莫名其妙的Crash。因为网络线程中没渲染所要的opengl上下文,会招致渲染出题目如果Crash。

6.6包大小

  • 采用压缩格式的纹理/音频
  • 尽心尽力不要使System.Xml而动于小之Mono.Xml
  • 启用Stripping来弱化多少仓库底尺寸
  • Unity strip level(strip by byte code)
  • Unity3D输出APK,取消X86架构
  • iOS Xcode strip开启

6.7耗电

下影响耗电之几乎独要素以及震慑过摘自号里的平首文章。

7.异常与Crash

7.1防御式编程

  • 黑的输入被保障你的主次
    • 自我批评各级一样输入参数
    • 检查来外部的数/资源
  • 断言
  • 错误处理
  • 隔栏
    防不胜防,不管如何守护总有失手的下,这便得很捕获和举报。

7.2要命捕获

是因为众多荒谬并无是发在支付工作者调试阶段,而是在用户还是测试工作者用阶段;这即得相关代码维护工作者于程序非常捕获收集现场消息。异常及Crash的监察与申报,这里不介绍Bugly的施用,按照apollo或者msdk的文档接入即可,没有最好多足说的。这里要通过Bugly介绍手游的几好像非常的捕获和分析。

Unity3D C#臃肿异常捕获

相比比较简单使用:<pre>code>Application.RegisterLogCallback/Application.RegisterLogCallbackThreaded</code></pre>
挂号回调函数/在一个初的线程中调用委托。特别注意:包项目受到独发一个Application.RegisterLogCallback注册回调,否则后面注册之会见挂前注册的回调!拨调函数吃stackTrace参数包蛮调用栈。
<pre><code>
publicvoidHandleLog(stringlogString, stringstackTrace, LogType type)
{
if(logString == null|| logString.StartsWith(cLogPrefix))
{
return;
}
ELogLevel level = ELogLevel.Verbose;
switch(type)
{
caseLogType.Exception:
level = ELogLevel.Error;
break;
default:
return;
}
if(stackTrace != null)
{
Print(level, ELogTag.UnityLog, logString + “\n”+ stackTrace);
}
else
{
Print(level, ELogTag.UnityLog, logString);
}
}</code></pre>

Android Java层异常捕获

try…catch显式的抓获异常一般是勿招游戏Crash的,它同时称之为编译时充分,即于编译阶段于处理的良。编译器会强制程序处理所有的Checked异常,因为Java看这好像非常都是可为处理(修复)的。如果没有try…catch这个那个,则编译出错,错误提示类似于”Unhandled
exception type xxxxx”。
UnChecked异常而称之为运行时非常,由于没相应的try…catch处理该特别对象,所以Java运行条件将会见停止,程序用脱离,也便是咱所说之Crash。那为什么未见面加于try…catch呢?

  • 没辙拿装有的代码都长try…catch
  • UnChecked异常通常还是较严重的要命,或者说就磨损了运转环境的。比如内存地址,即使我们try…catch住了,也无能够明显知晓什么处理该大,才能够保证程序连接下去的运行是科学的。

Uncaught异常会招应用程序崩溃。那么当崩溃了,我们是不是好做来什么啊,就比如Application.RegisterLogCallback注册回调打印日志、上报服务器、弹窗提示用户?Java提供了一个接口给我们,可以就这些,这就是是UncaughtExceptionHandler,该接口含有一个纯虚函数:
<code><pre>public abstract void uncaughtException (Thread
thread, Throwableex)</code></pre>
Uncaught异常发生常会见终止线程,此时,系统就会通知UncaughtExceptionHandler,告诉它深受停止之线程以及相应的不胜,然后便会调用uncaughtException函数。如果该handler没有让显式设置,则会调用对应线程组的默认handler。如果我们要捕获该老,必须实现我们温馨的handler,并透过以下函数进行设置:
<code><pre>public static void
setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler
handler)</code></pre>
特别注意:多次调用setDefaultUncaughtExceptionHandler设置handler,后面注册的会见盖前注册之,以最终一蹩脚为遵循。实现从定义的handler,只需要继承UncaughtExceptionHandler该接口,并促成uncaughtException方法即可。
<code><pre>static class MyCrashHandler implements
UncaughtExceptionHandler{

@Override  
public  void uncaughtException(Thread thread, finalThrowable throwable) { 
// Deal this exception  
}  

}</code></pre>
于另外线程中,都可以经setDefaultUncaughtExceptionHandler来设置handler,但当Android应用程序中,全局的Application和Activity、Service都与属UI主线程,线程名称默认为”main”。所以,在Application中当为UI主线程添加UncaughtExceptionHandler,这样全方位程序中之Activity、Service中出现的UncaughtException事件都得以为处理。
捕获Exception之后,我们尚亟需了解崩溃堆栈的音信,这样有助于我们解析崩溃的来由,查找代码的Bug。异常对象的printStackTrace方法用于打印好的仓库信息,根据printStackTrace方法的出口结果,我们可找到非常的源头,并跟踪至异常联机沾的进程。

public static String getStackTraceInfo(finalThrowable throwable) {  

    String trace = "";
    try{  
        Writer writer = newStringWriter();
        PrintWriter pw = newPrintWriter(writer);
        throwable.printStackTrace(pw);
        trace = writer.toString();
        pw.close();
    } catch(Exception e) {
        return"";
    }
    return trace;
}

Android Native Crash

眼前我们明白得编写和动C/C++原生插件,除非C++使用try…catch捕获异常,否则一般会直接crash,通过捕获信号进行处理。

iOS 异常捕获

跟Android、Unity类似,iOS也供NSSetUncaughtExceptionHandler
来开特别处理。

#import "CatchCrash.h"
@implementation CatchCrash
voiduncaughtExceptionHandler(NSException *exception)
{
// 异常的堆栈信息
NSArray *stackArray = [exception callStackSymbols];
// 出现异常的原因
NSString *reason = [exception reason];
// 异常名称
NSString *name = [exception name];
NSString *exceptionInfo = [NSString stringWithFormat:@"Exception reason:%@\nException name:%@\nException stack:%@",name, reason, stackArray];
NSLog(@"%@", exceptionInfo);
NSMutableArray *tmpArr = [NSMutableArray arrayWithArray:stackArray];
[tmpArr insertObject:reason atIndex:0];
[exceptionInfo writeToFile:[NSString stringWithFormat:@"%@/Documents/error.log",NSHomeDirectory()]  atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
@end

而内存访问错误、重复释放等悖谬引起崩溃就无法了,因为这种错误它抛来之凡信号,所以还得使专门做信号处理。

windows crash

相同windows提供SetUnhandledExceptionFilter函数,设置最高一级的特别处理函数,当次出现任何不处理的十分,都见面接触发而设置的函数里,然后以雅处理函数中拿走程序非常时之调用堆栈、内存信息、线程信息相当。

8.适配同配合

8.1UI适配

  • 锚点(UIAnchor、UIWidgetAnchor属性)
  • NGUI UIRoot统一安装缩放比例
  • UIStretch

8.2兼容

  • shader兼容:例如if语句有的机型支持不好,Google nexus
    6在shader中采用了if就见面crash
  • 字兼容:android复杂的环境,有的手机厂商与rom会对字体进行优化,去掉android默认字体,如果不打包字体会不具体中亲笔

9.调剂和开发工具

9.1日志与跟踪

事实证明,打印日志(printf调试法)是杀实用之不二法门。一个吓用底日志调试,必备以下几个作用:

  • 日志面板/控制台,格式化输出
  • 没完没了级别(verbosity level):ERROR、WARN、INFO、DEBUG
  • 频道(channel):按功能等进行模块划分,如网频道特接/显示网络模块的音,频道建议下枚举进行命名。
  • 日记同时会输出及日志文件
  • 日记上报(iOS屏蔽文档目录,出了问题为以不顶日志)

9.2调试用绘图工具

调剂绘图用工具指开发暨调试中为可视化的绘图用工具,如腾讯桌球开发调试时会使VectrosityScripts可视化球桌的情理模型(实际碰撞线)帮助调节。这类工具得以省大量时日及迅速定位问题。通常调试用绘图工具包含:

  • 支撑绘制基本图形,如直线、球体、点、坐标轴、包围盒等
  • 支撑自定义配置,如颜色、粒度(线之粗细/球体半径/点的大小)等

9.3游戏内置菜单/作弊工具

每当开调试中提供戏进行中的片配置选和作弊工具,以利调试以及提高效率。例如腾讯桌球游戏被提供:

  • 娱乐内物理引擎参数调整菜系;
  • 改签到奖励领取天数等作弊工具

顾游戏内之所有开支调试用的家伙,都需经编译宏开关,保证发布版不会见把工具代码包含进去

9.4Unity扩展

Untiy引擎提供了怪强劲的编辑器扩展功能,基于Unity
Editor可以兑现大多之效用。公司里面、外部都起大的开源扩展可用
商家标,如GitHub上之:
UnityEditor-MiniExtension
Unity-Resource-Checker
UnityEditorHelper
MissingReferencesUnity
Unity3D-ExtendedEditor

商店里面:
TUT、BeautyUnity、UnityDependencyBy

10.类别运营

电动构建

  • 版本号——主版本号.特性版本号.修正版号.构建版本号
    • [构建版本号]应用分发平台提升判断标准
  • 机关构建
    • Android
    • iOS — XUPorter

合作社里面接入SODA即可,建议搭建好的构建机,开发中每天N
Build排队会死人的,另外也可以搭建好之搭建构建平台

统计申报

  • Tlog上报
  • 玩家转化关键步骤统计(重要)
  • Ping统计上报
  • 打业务的统计报告(例如桌球球局相关的统计申报)
  • 灯塔自定义上报

营业模板

  • 配置化
  • 服务器动态下
  • CDN拉取图片并缓存

上线前的checklist

项目 要点 说明 指标
灯塔上报 1. 灯塔自带统计信息 2. 由定义信息举报
灯塔里面含有众多统计数据,需要检查是不是ok 1. 本子/渠道散布 2.

下频率统计 3. 留存统计(1天是、3天在、7上在、14上在) 4.
用户结构统计(有效用户、沉默用户、流失用户、回流用户、升级用户、新增用户)

  1. 硬件统计(机型+版本、分辨率、操作系统、内存、cpu、gpu) 6.
    Crash统计(Crash版本、Crash硬件、Crash次数等)等等
    信鸽推送 | 能够针对单个玩家,所有玩家推送消息| |
    米大师支付 | 正常开支 | |
    有惊无险组件 | 1. TSS组件连通抱 2.
    潜藏其中符号表:C++开发之代码应用strip编绎选项,抹除程序的号 3.
    关键数据加密,如影变量+异或加密算法项 | 根据安全基本供的文档完成具有
    | 接入安全组件,并由此平安中心的验收
    泰 | crash率 |
    用户crash率:发生CRASH的用户数/使用用户数;启动crash率:启动5S内产生crash用户数/使用用户数
    | 低于3%
    已故网络 | | 断线重连考虑,缓存消息,重发机制等等 |
    客户端的主导场景必须来断线重连机制,并于生网络抖动、延时、丢包的纱场景下,客户端需上以下要求:一.
    未可知起以下状况:1、游戏受无能够出现收支不等、客户端卡死/崩溃等异常情况;2、游戏中心力量(如登录、单局、支付相当于)不能够有导致游戏无法正常开展的UI、交互问题;3、不可知出危害玩家利益或者可为玩家额外获利的题目;4、需要发客观之重连机制,避免每次重连都回去到登录界面。二.
    需要对延时之情形时有发生对应的提拔
    兼容性 | | |通过适配测试
    游玩更新 | 1. 整包更新;2. 增量更新 | |
    特别说明:iOS送审版本支持连特定环境,与正规环境区别开,需要通过服务器开关控制
    性能 | 内存、CPU、帧率、流量、安装包大小 |
    |【内存占用要求】Android平台:在对应档次客户端最低配置以上,均用满足以下内存消耗指标(PSS):
  2. 档案机型指标:最高PSS<=300MB
    (PSS高于这个专业会影响28%用户之体会,约1800万)
    2.
    档机型指标:最高PSS<=200MB(PSS高于这个正式会潜移默化45%用户之体验,约3000万)
    3.
    档机型指标:最高PSS<=150MB(PSS高于这个标准会潜移默化27%用户之经验,约1800万)
    iOS平台:在对应档次客户端最低配置以上,均用满足以下内存消耗指标(PSS):
  3. 档案机型指标:消耗内存(real
    mem)不大于250MB(高于这个正式会影响53%用户之心得,约1900万)
  4. 档案机型指标:消耗内存(real
    mem)不大于200MB(高于这个标准会影响47%用户之体会,约1700万)
    【CPU占用要求】Android平台:CPU占用(90%)小于60%
    iOS平台:CPU占用(90%)小于80%
    【帧率要求】
    1.
    档机型(CPU也四审结1.4GHZ,RAM为2G)或以上机型:游戏为主玩法中,最小FPS应休低于25帧/秒
    2.
    档机型(CPU为有限审核1.1GH,RAM也768M)或上述机型:游戏中心玩法中,最小FPS应无小于25帧/秒
    3.
    档机型(CPU也1GHZ,RAM为768M)或以上机型:游戏中心玩法中,最小FPS应休低于18帧/秒
    【流量消耗要求】游戏中心玩法流量消耗情况(非一次性消耗)应满足以下条件:
  5. 于分局的一日游场景,单局消耗流量不越200KB
  6. 对非分局游戏场景或流量及局时有关的光景,10分钟消耗流量不超越500KB |

发表评论

电子邮件地址不会被公开。 必填项已用*标注