Unity手游实战:从0开始SLG——客户端技术选型

项目背景git


所谓选型,我认为就是为了实现某(些)个需求或者解决某(些)个问题所使用的解决方案。它多是一个技术方案,也多是一个管理方案,也能够是一个软件、工具或者是流程规范。github


这篇的主题是技术选型,因此主要会分析项目客户端部分的技术解决方案。那么作选型分析以前就要先收集需求,分析需求,搞清楚咱们的项目须要什么,达到什么效果,实现什么功能。除了项目自己章程和范围以外,还要看同项目的其余部门分工和合做方案(好比服务器的技术对接,美术资源的规范流程以及部分效果的实现对接),在这以外还须要考虑公司的大环境,好比资源的支持程度,项目的编制和人员的支持力度,开发环境甚至是市场和法律条件。编程

世界地图缓存

咱们如今的项目是一个沙盘类型的SLG。主要玩法也是世界地图的资源掠夺。这是比较经典的SLG玩法,包括《列王纷争》《王国纪元》《乱世王者》《真龙霸业》等等国内外数据良好的游戏都是这种核心玩法。大地图很大,一个大服甚至会有几十万的地形数据。在地形编辑、行军寻路(须要支持关隘和高地)、服务器数据同步等诸多地方都会有比较大的挑战。服务器

主城微信

接下来是城市发展。这部分和市面上大多数的同类型游戏设计都不同了。大部分的沙盘类游戏都是采用静态城市的布局策略,即每一个建筑的坑都留好了,你达到等级以后只要点击坑位建造指定的兵营、伐木场、训练营等建筑就行了。网络


并且建筑自己也只是一个功能系统的入口,并不会有建筑和建筑之间产能影响,亦或是按照本身的习惯建造建筑和道路。咱们的建筑后期加起来会有140多个,每一个建筑都是能够自由移动和布局,在这个功能点上更像《部落冲突》的表现形式。架构


可是和它不一样的是,咱们的主城不会参与战斗,因此也不会有防护性的建筑,取而代之的是服务器性的建筑,好比水井、医院、教堂、公园等等这些服务器性的建筑会影响到生产建筑的产能,因此相同等级的状况下,合理的布局会让你的产能超出别人一截。另外咱们的道路也是能够自定义编辑的,和道路相连的建筑也会有加成。若是有人玩过《城市:天际线》应该可以更明白一些主城的玩法模型。可是和《天际线》相比咱们又没有那么复杂的计算和影响,毕竟人家是PC上的纯单机模拟养成类型。app


除了建筑和道路的的自由编辑以外,NPC也是主城的主要功能。NPC会有10几种,每种的AI都不同,而且要求可以在两个彻底紧连的建筑缝隙中穿插和移动,还要考虑道路优先。框架


城市会有自定的保存模板,还要有可破坏和不可破坏的装饰机制等等。


因此主城的难点除了实现各个功能以外,还须要解决100多个建筑+几十个NPC+场景自己和UI部分的全部性能消耗。

战斗

战斗以前也有说过,须要支持同屏500+的单位同时战斗,这些单位每一个都是独立的个体。这表示,每一个AI都须要有本身的AI机制和独立的动做表现。一个单位大概会有5-6种动做,小型单位600+面,大型单位1000+面。这对GPU和CPU的压力都很是的大。

战斗还须要支持录像回放,而且在任何设备任什么时候候播放出来的结果和过程都要一致。

战斗须要支持倍速功能。


若是有人熟悉《全面战争》系列会比较容易理解咱们战斗模式。不过和全战不一样的是,咱们的士兵在出战以后就不能手动控制了,毕竟是移动游戏,太复杂了伤害玩家。。。不过,其实仍是能够手动释放英雄技能的。【手动滑稽】


三块重点内容分析完成以后,技术方案就须要根据需求去挑选了。用一句归纳游戏就是:轻经营、重策略的沙盘SLG。

技术选型

嗯,下面就正式入活了。

引擎版本

技术选型要服务于产品。但在挑选技术方案以前还要作一件事情,引擎版本的选择。


早在2018年底,咱们就收到了谷歌商店上架APP强制要求64位版本的需求,具体强制时间在19年8月1日。当时和Unity团队沟通的时候,反馈是必须2018以上的版本才能支持64位(不过一段时间以后又说2017.3也能够)。加上当时手里有Unity2018.3的引擎源码,因此就把版本定在了Unity2018.3(不过将来可能会升级到2019,里面有分帧GC的功能我会比较感兴趣)。


版本选定以后,就开始真正的技术选型了,这里我大体罗列了一下,其中有些是框架方向,有些是工具插件,有些是设计思路。但整体仍是囊括了客户端该有的技术部分。

Sproto

网络游戏,首先要考虑的是如何与服务器进行通讯。做为SLG类型,对于响应速度需求并不会像FPS或者MOBA类型那么的强烈。因此就挑选了TCP的方式进行链接。而后,使用了Sproto做为协议的载体进行消息传递和RPC封装。


TCP的部分就不用过多讲解了,作网络游戏都会接触和了解。这里讲一下Sproto。可是在讲Sproto以前呢,还必须先拓展另一个东西:skynet


skynet是云风大神建立的开源服务器框架,使用C和Lua结合的技术搭建的基于Actor模式的引擎。这里不会拓展讲解skynet的技术细节,有兴趣的能够去看下我同事对于skynet的源码赏析。


回到刚才SProto的问题上来,Skynet原本是支持PB(proto buffer)的。可是只支持2.X的版本,而且已经再也不维护了。出于优化的目的,skynet使用了一套自定义的格式Sproto。它实际上是基于proto的改良,将proto里的冗余表达进行了简化,让它更知足于skynet在Lua端的性能表现。那么咱们综合考虑下来也是选取了sproto的方式进行协议传输。


这其实又涉及到一个问题,Sproto实际上是设计个skynet用的,可是客户端用的是Unity,开发语言是C#,确定不能直接使用。不过不要紧,我央求了服务端大佬给咱们写了C#的转译工具,能够将Sproto的描述文件转为CS文件,而后再写了一套序列化和反序列化工具,呃~能够像PB同样正常序列化了。

通常客户端关心数据分为两个部分,一个部分来自于服务器端,另外一个部分来自于策划配置表。如今网络端搞定了,数据表怎么办呢?对,我又去央求了咱们的服务端大佬,给咱们写了一个excel转Sproto的工具(过程很是复杂。。嗯先把Excel转成Lua格式,再Lua转成Sproto的描述文件,再把描述文件转为CS),这样咱们的策划数据也搞定了。

GPUSkin+GPUInstance

咱们的战斗场景须要显示500+的单位,每一个单位携带本身独立的AI和动做。大型单位约有100面,小型约600面。那么同屏显示以后,CPU和GPU都面临巨大的性能压力。用小米5S作过一次测试,当使用skinmesh的时候,4000单位的帧率就只能到20了,换了GPUSkin方案,8000个单位仍然可以保持50帧。这部分的选型是为了解决同屏渲染压力。

ECS

与传统的面向对象的编程理念不同,ECS(Entity-Component-System)是面向数据的编程思想。若是不理解概念的能够本身先去翻阅下资料,也能够等后面讲技术细节的时候再去了解。这里简单的类比一下帮助理解。就比如Unity的开发模式,一个GameOject能够理解为一个Entity,单独放在场景里它什么都不是。若是你给它绑定了一个Text组件,那么它立刻就会变成一个Text 组件;若是绑定一个Button组件那么它就是一个Button。那么这个时候你能够理解为Unity就是一个EC的思想。至于为何引入S的概念就是为了解决耦合和数据冗余。让一个Component里只有数据而没有方法,全部的方法都写在System。让数据在内存里的排布更加紧密,增长缓存命中率,特别善于处理大批量的数据。


同时,由于数据和系统分离,那么作回放的时候数据很是便于保存。这又符合了咱们常规的逻辑和表现分离的设定,因此这套机制完美契合了咱们战斗需求。配合GPUSkin和GPUInstance既优化了性能,又能实现回放和解耦,同时还会带来另一个优点,逻辑和表现分离。


咱们还作了一个大胆的尝试,将逻辑和表现分离以后,将逻辑层接入到服务器中(服务器是基于Actor的,因此扩展一个战斗服很容易),客户端则既跑逻辑又跑表现。这样带来的好处就是,只要咱们给定的输入一致,由于逻辑是一套,跑出来的结果也一定一致。因此世界离线战斗的时候咱们调用服务器秒算结果,PVE副本的时候,客户端展示战斗过程,很是美妙。

XLua

Lua在客户端集成的主要做用仍是用来解决热更新问题的,它带来了便捷的同时固然也带来了性能问题。通常来讲,Lua和C#的性能差距在40倍左右。移动开发一路走来有不少Lua相关的框架,好比toLua,uLua,slua,Xlua等。


因此有的时候就会想,有没有既能够实现热更新又能提升性能的方法,那么Xlua就是这种。开发用C#,热更新修复用XLua。固然这也不是彻底免费的,取而代之的是要在开发的过程当中作好各类标识,增长了开发管理难度同时包的代码段会增加不少。

说点题外话,移动游戏刚起步的阶段,除了Lua以外确实没有更好的热更新手段。因此你们才考虑将Lua接入到开发中,甚至一度接管项目的总体外围开发。可是如今除了Lua以外,也还有不少其余方式能够作到热更新,好比腾讯的潘多拉。固然项目的开发过程当中要使用防护性编程是确定的,除了作好各项QA验收以外,还要对每一个功能作出屏蔽入口,甚至在一些运营活动上作好模板参数,能够经过快速调节参数就能变成另一个活动。


咱们使用XLua的想法也会趋近于这个思惟。平时开发都会在C#上,可是仍然会在Lua层面维护一整套的功能系统,让Lua层面有能力解决大部分的突发状况和新增需求,可是这仅仅是一个后备手段。全部一切仍是以C#为主,哪怕是上线阶段用lua修了某些问题,那么再下一个版本里也会把功能修复到C#层面,并从lua层移除。

UGUI

这个其实如今可选择性不是很大。目前能与之一战的是NGUI和FairyGUI。NGUI和UGUI是一个爸爸,可是在层级处理方面十分复杂,对于一些新手小朋友的理解尚不友好,不像UGUI保证在一个Canvas下能按照树状层级显示。FairyGUI是一个第三方的GUI,它须要接入SDK。而且它本身内部保证接入了SDK会在不一样平台表现一致。这对于可能须要转引擎(COCOS转Unity之类的)的项目可能更好,可是咱们并不会转因此并不须要。

Wwise

Wwise是一个音效框架,其实这里能选择的余地不大,基本就是fmode和Wwise两种。可是近几年fmode有些没落,操做、性能和工具链都跟不上了,之前但是一枝独秀。

GCloud

GCloud是腾讯云产品的一种,起初是为了服务内部游戏产品所孵化出的统一平台。


国内游戏经常使用的游戏内语音,电台等均可以接入这个实现。另外功能还覆盖了游戏更新,区服导航,微端puffer等游戏内经常使用的功能设定。


这一套接入起来真真儿是极好的,为手游的几个难搞部分提供了统一化的服务,后台的操做也是极其简单,有兴趣的能够去官网了解。

Addressable Asset System

这套东西是我目前极力推荐的,它起于2018版本(预览版),在2019已是正式版本功能,提供了一套极其强大的资源打包和加载的管理方案。


以往咱们的资源打包方案都须要本身去实现,诸如在编辑器下使用编辑器接口,在实机状态下打包成bundle形式加载,而后还须要咱们本身去收集和管理资源的依赖关系,维护自定义的资源列表,而这套通通帮咱们作好了,而且提供了可视化的界面操做,管理资源妈妈不再用为我费心了。


依稀记得4.x的版本,要作资源管理须要本身指定目录或者资源,而后根据是不是依赖项的方式调用打包的API。甚至若是作资源更新,你须要本身维护一份资源列表,本身自定义MD5值比对差别,若是须要告知用户下载的资源大小,你还要本身统计单个资源的大小,汇总告知玩家。


5.X的时候,资源管理作过一次大的升级,让每一个资源都带有Asset Bundle标签,这样在Unity的工程目录就能够经过自定义标注资源的方式标识资源,而且在生成的每一个bundle的同时为bundle生成一个manifest文件,用来标识该bundle的内容和依赖项等大概长这样:


在运行时进行资源加载的时候也是先加载这个文件查找依赖项,递归加载直至完成。比起4.x以后确定是好了不少,可是仍然是极度的麻烦。


如今是一个这样的可视化面板,全部资源均可以经过拖拽完成,另外代码里也提供了完整的加载方案,让你在编辑器和真机的都不用关心资源格式只使用同一个接口调用就好。


详细的技术内容会放在指定章节去讲解。

Tiled

Tiled是一个老牌的基于瓦片的2D编辑器。功能很是之强大,以致于我就不在这里讲述它的强大之处了。


其实Unity2017以后也针对性的提供了tileMap功能组件,用于给2D游戏提供一些周边辅助。甚至在github上还提供了扩展笔刷和Demo来支撑。但尽管如此,它在功能实现上仍是不如Tiled来的快捷。


另外咱们的世界地图很是之大,有几十万格,因此单用模型或者地形去刷就太耗费资源了。因此这里会选用2D的方式来展示世界地图的地形,至于地图上的奇观、主城、资源点、玩家部队、怪物等等就用3D的形式去展示。


Tiled编辑器生成的格式Unity并不能直接用,因此还须要借助一些插件,这个咱们放在后面去讲解。

TimeLine

这个很简单,是2017之后提供的一套线性编辑工具,咱们有可能会在剧情,镜头等方面使用它,另外它和cinemachine是一对CP,成对出现。

TextMeshPro

TMP是早在5.x就存在的一个优秀插件,后来由于表现过于优异被吸取为Unity正式功能。咱们都知道UGUI对于字体计算上很是的耗时。同时UGUI的渲染原理也决定了对于一些常常变更的UI节点有着较大的性能问题。因此对于一些战斗飘字,小地图、聊天等变动频繁功能来讲,UGUI表现是很是糟糕的。另外UGUI对于字体的内存处理上面也是有比较大的问题的,当字号不一致或者差别大的时候,内存消耗严重,这在聊天功能里表现尤为显著。


另外还有一个问题是聊天图文混排,这个在NGUI里作的比较好,可是UGUI自己却不支持,不过不要紧TMP支持!


除了支持图文混排以外,它还支持各类富文本,超连接、相似平方的上标,化学表达式的下标、 各类文字效果好比打字机或者遮罩等等很是强大。


而且最重要的是,它能够和UGUI彻底混用,甚至直接替代UGUI里的Text、DropDown、Inputfield等使用到文字的组件。


嗯,它除了处理文字以外,咱们的血条,建筑头顶图标等各类HUD也能够用它表现,是否是很是惊奇!

A*PathFinding

这个就是前段时间翻译的的A*PathFinding 教程系列。以前的总结篇也有对这个插件作过总结,总的来讲这是一个很是很是值得推崇的插件。不只仅在于它的功能强大,也在于它的软件架构,和文档教程支持程度。是一个教科书般的第三方库。


咱们的战斗其实并无用到寻路模块,可是在表现层须要作动态规避。由于对于逻辑层(服务器运算的时候)来讲,单位是没有碰撞和体积的,可是对于客户端来讲,咱们确定不能让单位所有重叠在一块儿,这就使用到了A*插件的动态规避(RVO)。


主城部分由于涉及到经营,那么就必须模拟大量的NPC行为,有NPC就要有各类寻路和目标表现,好比一个送牛奶的农夫,去公园玩耍的孩子,送货的、送酒的,去市政厅办事的,去医院看病的,城里巡逻的等等。那么寻路这块就极为重要。


世界地图这块咱们也涉及到行军,由于咱们会考虑作关隘和高地,因此须要使用到分层寻路。另外与主城的NPC表现不同的是,主城是装饰性的NPC,而且人物比较小,因此动做幅度和寻路状态机械一点反而好看,可是世界地图是功能性的,虽然建筑和资源点都是基于网格的,可是咱们计算路径的时候却不能使用网格,会影响到行军的时长和路径。由于行军是由服务器计算的,因此这块咱们的打算也是制做一个世界地图的寻路系统库,而后丢到服务器去跑,也就是说功能是客户端作,可是丢在服务器去运行,是否是很酷。

收尾

选型是个很大的课题,这篇文章只讲了技术部分的方案,后面会针对各类技术细节作探讨,以及讲解项目中遇到的实际问题从什么维度去思考解决方案,但在这以前还须要先讲一下客户端的目录分布。看看一个实际的大项目是怎么在几十我的之间合做有序,各司其职的。


PS:这篇文章是去年10月份发表在知乎上的,其中涉及的技术选型是在项目立项以前就作好的。如今项目开发已经接近尾声,除了GCloud这个部分由于市场缘由没有接入以外,其他项目技术均在预约的选型之下。


本文分享自微信公众号 - 壹种念头(OneDay1Idea)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索