兵马俑(0.05折三国免费版) 兵马俑(0.05折三国免费版)

Unity3D手游开发实践《腾讯桌球》客户端开发经验总结

Unity3D手游开发实践《腾讯桌球》客户端开发经验总结

本次分享总结,起源于腾讯桌球项目,但是不仅仅限于项目本身。虽然基于Unity3D,很多东西同样适用于Cocos。本文从以下10大点进行阐述:

1.架构设计

2.原生插件/平台交互

3.版本与补丁

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

5.资源管理

6.性能优化

7.异常与Crash

8.适配与兼容

9.调试及开发工具

10.项目运营

1、架构设计

好的架构利用大规模项目的多人团队开发和代码管理,也利用查找错误和后期维护。

框架的选择:需要根据团队、项目来进行选择,没有最好的框架,只有最合适的框架。

框架的使用:统一的框架能规范大家的行为,互相之间可以比较平滑切换,可维护性大大提升。除此之外,还能代码解耦。例如StrangeIOC是一个超轻量级和高度可扩展的控制反转(IoC)框架,专门为C#和Unity编写。已知公司内部使用StrangeIOC框架的游戏有:腾讯桌球、欢乐麻将、植物大战僵尸Online。

依赖注入(Dependency Injection,简称DI),是一个重要的面向对象编程的法则来削减计算机程序的耦合问题。依赖注入还有一个名字叫做控制反转(Inversion of Control,英文缩写为IoC)。依赖注入是这样一个过程:由于某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点。在程序运行过程中,客户类不直接实例化具体服务类实例,而是客户类的运行上下文环境或专门组件负责实例化服务类,然后将其注入到客户类中,保证客户类的正常运行。即对象在被创建的时候,由一个运行上下文环境或专门组件将其所依赖的服务类对象的引用传递给它。也可以说,依赖被注入到对象中。所以,控制反转是,关于一个对象如何获取他所依赖的对象的引用,这个责任的反转。

StrangeIOC采用MVCS(数据模型?Model,展示视图?View,逻辑控制?Controller,服务Service)结构,通过消息/信号进行交互和通信。整个MVCS框架跟flash的robotlegs基本一致。

数据模型 Model:主要负责数据的存储和基本数据处理

展示视图 View:主要负责UI界面展示和动画表现的处理

逻辑控制 Controller:主要负责业务逻辑处理,

服务Service:主要负责独立的网络收发请求等的一些功能。

消息/信号:通过消息/信号去解耦Model、View、Controller、Service这四种模块,他们之间通过消息/信号进行交互。

绑定器Binder:负责绑定消息处理、接口与实例对象、View与Mediator的对应关系。

MVCS Context:可以理解为MVC各个模块存在的上下文,负责MVC绑定和实例的创建工作。

腾讯桌球客户端项目框架

代码目录的组织:一般客户端用得比较多的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。

公司组件

msdk(sns、支付midas、推送灯塔、监控Bugly)

apollo

apollo voice

xlua

目前我们的腾讯桌球、四国军棋都接入了apollo,但是如果服务器不采用apollo框架,不建议客户端接apollo,而是直接接msdk减少二次封装信息的丢失和带来的错误,方便以后升级维护,并且减少导入无用的代码。

第三方插件选型

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 和函数。?用?static?和?extern?修饰符标记方法。

调用 DLL 函数。像处理其他任何托管方法一样调用托管类上的方法。

在C#中创建回调函数,C/C++调用C#回调函数

创建托管回调函数。

创建一个委托,并将其作为参数传递给?C/C++函数。平台调用会自动将委托转换为常见的回调格式。

确保在回调函数完成其工作之前,垃圾回收器不会回收委托。

那么C#与原生插件之间是如何实现互相调用的呢?

1.将源码编译为托管模块;

2.将托管模块组合为程序集;

3.加载公共语言运行时CLR;

4.执行程序集代码。

注: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这里不深入介绍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。

发表评论