1.3 如何构建Unity3D项目
前面我们对软件架构进行了解释,并且对软件架构抽象的思维方式进行了详细介绍,包括分层、分治、演化。本书将具体介绍架构中的误区,以及如何做前端架构,并且了解如何构建Unity3D项目。
1.前端与后端架构之间的共性
前后端架构的目标都是高性能、高可用、可扩展、安全、可容错。对于前端来说,除了这些目标特性外,还需要加入更多的用户体验,包括视觉效果和操作灵敏度。
作为前端工程师,用户体验是比较重要的,这种体验涉及很多方面,包括性能、视觉效果,以及操作上的人性化等。例如,如何让游戏加载速度更流畅、如何制作更绚丽的特效、如何减少电量的消耗、如何最快地响应用户操作等。
前端技术与后端技术都是在同一个系统层面上建立起来的,都是建立在Linux、Windows、Android、iOS操作系统之上的,两者最后都需要开发者了解操作系统的接口以及底层运作原理。它们的区别在于,后端在操作系统上构建了一套服务端框架,而前端在操作系统上构建了一个渲染引擎,它们需要在此之上做业务架构。我们自己构建或选择某个商业渲染引擎后,再在渲染引擎之上建立游戏应用的业务架构。因此,我们其实有两套架构要学习,一套是渲染引擎架构,一套是游戏业务架构。
对渲染引擎架构的探讨偏离了本书的范围,这里不做详细阐述。游戏业务架构中有很多需要我们搭建的框架,可以以模块形式来命名它们,包括网络框架、UI框架、数据框架、核心战斗框架、AI框架等,后面将进行讨论。
2.培养架构设计思维
良好的架构设计思维的培养,离不开工作中大量高质量项目的实战锻炼,以及平时的学习、思考和总结。
基本的架构设计思维在大学计算机课程(比如数据结构和算法)中可以找到,大学里以学习理论知识为主。回头看,基本的架构设计思维在那时就已经埋下种子,后面的工程实践会进一步消化和应用所学的理论知识,随着经验的积累,我们能够解决的问题的复杂性和规模逐渐变大,但所用的方法依然是抽象、分层、分治和演化。
架构设计并不是静态的,而是动态的。只有不断应对环境变化的系统,才是有生命力的系统。所以,即使你掌握了抽象、分层和分治这三种基本思维,仍然需要演化式思维,在设计的同时,借助反馈和进化的力量推动架构的持续演进。
架构师在关注技术、开发应用的同时,需要定期梳理自己的架构设计思维,积累的时间长了,看待世界事物的方式会发生根本性的变化,你会发现我们生活的世界,其实也是在抽象、分层、分治和演化的基础上构建起来的。架构设计思维的形成会对你的系统架构设计能力产生重大影响。可以说,对抽象、分层、分治和演化掌握的深度和灵活应用的水平,直接决定架构师解决问题域的复杂性和规模大小,是区分普通应用型架构师和平台型/系统型架构师的一个分水岭。
3.试着构建Unity3D项目
我们可以使用以上方法来试着构建Unity3D项目。下面采用分层的思维方式先确定架构的层级,如图1-8所示。
图1-8 Unity3D分层设计
把整个项目分成五大层级,即网络层、数据管理层、资源管理层、核心逻辑框架层、UI框架层。
这样一分就清晰地知道了我们需要做哪几大类的东西。只是这样拆分太笼统,特别是核心逻辑框架层,完全是概括性的层级,无法表达具体的系统。所以我们要再次拆分层级,把太过于笼统的层级进行分层,如图1-9所示。
经过分层后再采用分治的方法,把核心逻辑框架层拆成为工具编辑器、角色行为框架、AI框架、地图场景与寻路框架、着色器与特效、设备平台等。这些子层都在核心逻辑框架层中,它们有自己的框架,也可以互相调用,它们一起构成核心逻辑部分,也就是核心玩法或核心战斗的主要部分。
图1-9 分层后再分治
我们再将资源管理层和数据管理层进行拆分,分为AssetBundle资源管理和Prefab资源管理,以及内存数据管理和外部数据管理,这样更清晰地分工了各层的职能。其实还有很多其他的层级这里没有提到,包括常用库、工具库、动画控制等,这里暂不一一提出。
在游戏项目中,最常用的是数据表、网络层、UI层、常用库这几个模块。我们可以使用这种层级的方式来试着搭建一个完整的项目,只是做抽象的编写,就可以清晰地知道这个项目需要哪些模块和层级。
比如,如果项目是单机的策略类游戏,可能就没有很多角色上的东西,而多了很多2D动画行为控制上的需求。这时在进行层级划分时,就可以把注意力重点放在2D动画行为控制、UI框架、数据管理、资源管理及AI上。
如果项目是以3D人物角色为主的网络游戏,则应有地形地图、角色行为控制等内容,此外,还需要一套角色技能、特效、动画编辑工具等。网络游戏项目前期我们会对网络这块内容进行决策,确定是TCP-Socket、UDP还是Web形式的HTTP。3D MMRPG的难度主要集中在了解角色技能动画、AI、地图、物理模拟上。我们可以把重点提取出来,让擅长的同事专门做这块内容的深度挖掘,把最难把控的放在最优先的位置去做,再对这些层级进行细致化构建。
对模块进行细致化构建时,我们可以使用分治法去构建。如果某个要解决的问题已经确定,且这个问题的规模太大,无法直接下手解决,那么可以使用分治法,把一个问题分成几个小问题来处理,把小问题再划分成更小的问题,直到能直接解决为止,再依次对它们进行处理。
下面拿网络层的设计来说,对它进行分而治之的设计如图1-10所示。
图1-10 网络层设计
图1-10中,我们把网络层拆分成HTTP、TCP-Socket、UDP这三种类型的形式,再对每个类型的具体接口进行拆分,对于拆分出来的每个接口,如果还不能直接使用,则再进行细致的拆分,直到拆分到可以具体实施为止。在图1-10中,我们以接口的形式进行拆分,先将接口拆分成连接、断开连接、发送数据、收到数据,以及(断开、连接、终结)网络事件,然后再对每个接口进行拆分,把接口需要处理的问题拆分出来各个击破。
除了我们列举的网络层外,其他层级部分的框架也可以用同样的方式进行分解,即用分而治之的方法逐个击破每个模块。下面描述了各个模块的拆分原则。
·数据表:EXL导为二进制文件、JSON或其他格式,读取接口和解析接口的定义。
·UI层:确定是使用NGUI还是UGUI,并针对界面基类、界面管理、输入事件封装进行选择,且自定义通用组件基类和各类通用组件。
·外部资源管理:确定是否使用AssetBundle,是否对AssetBundle资源进行分类,是否依赖AssetBundle资源间的关系,是否加载与释放AssetBundle的管理,是否加密AssetBundle。
·AI层:确定是使用状态机还是行为树或者其他,以及状态机或行为树接口的实现、AI可视化工具、AI扩展接口。
·地形地图:包括地图是2D的还是3D的,场景编辑器的结构是否需要网络合并,场景内的大小物件区别对待,大地形在游戏里如何显示,是否要划分区块。
·寻路与网格:确定是使用A星算法还是跳点算法或者其他算法;是使用网格栅格还是三角网格;是使用长距离寻路的解决方案还是地图数据管理。
·常用库:包括时间函数、数学函数、数字变量加密封装、坐标转换函数、Debug调试工具、各大逻辑系统通用工具等。
·角色行为控制:包括人物移动处理方案、摄像机的碰撞检测、动画特效编辑器、技能编辑器、行为流的建立。
·2D动画控制:包括动画组件封装、2D动画的制作流程、2D图合并为图集。
实际工作中,我们对层级和模块逐个攻破的同时,也进入了架构演化模式。在一开始构建的架构中,某部分的设计可能并不十分合适,在后面的工作中我们需要对其逐步修复、完善甚至替换,这些都是演化的重要步骤。
在不断完善架构的过程中,原本简单抽象的架构开始复杂化。每个模块都在有条不紊地演进,也会不断冒出各种各样不适应或者不符合实际需求的问题,我们需要及时跟进演化内容,包括剔除、重构、改善、修补前面由于各种原因而导致的错误。
最后应该注意架构设计的文档要及时跟进完善,在抽象的过程中,我们需要整理和记录整个过程,以便在今后完善架构时有途径获得前面在做决策时所考虑的各方面问题。