转载:https://blog.csdn.net/langresser_king/article/details/40409445
新的公司新项目的手游客户端框架我并不是十分赞同,虽然最终我妥协了,并且为自己竟然做出质疑上司这样的幼稚行为而后悔。但是就最近写的一些代码来看,我更加坚定我自己的思路和想法。当然我的习惯和思路不一定适合其他人,所以我并不会说其他人的思路或者习惯不对或者不好,只要能用清晰的思路写出清晰的代码就好了。
一、3D、Unity意味着更长的项目周期?
很多人都会有这样的看法,因为Unity比cocos2d-x功能更多,坑更多,所以会加长项目的开发周期。也有一些技术向的人会认为,Unity优化不到位,所以性能上还做不了大的mmo,甚至在性能上还不如OGRE。 我无法用井底之蛙来形容这些人,因为这些人都是项目负责人、技术总监、资深主程。但是在我看来项目开发的进度是跟团队水平有关的,跟引擎无关。现在很多线上的成功的3D MMO手游也证明了Unity的性能。 根本不存在“做不了”这样的说法,甚至“坑太多,很难做”这样的说法都不成立,因为如果它真的那么“坑”的话,就不会有如此多的公司选择Unity,而且成功的开发出千万收入级别的游戏了。
半个月前我还认为2.5D游戏随便写,因为最早在网龙时使用C3引擎就很安心、很顺手,而且我看过C3的源代码也不多,几百k而已。不过现在看看cocos2d-x的进度,以及我们自己对3D光效这块儿的移植,cocos2d-x成为一个合格的2.5D游戏引擎还需要至少一年时间。当然技术牛的团队可以自己去搞去优化,但是我实在想不出这么做有多少意义,使用Unity更加安心和方便。比如你不用操心光效的运行效率、引擎的稳定性、模型导入和渲染、模型动画处理、后期特效等等,而这些都是我最近使用cocos2d-x的时候被恶心到的。只能说,我们认为它很简单,我们也认为我们的需求很简单,可是它就是做的不够好。
二、MVC框架真的适合手游客户端吗?
应该很多人都会认为是的,这是一个经典的解耦合的框架。但是我感觉,框架不是目的,是否真的解耦合也不是目的,是否遵循某种设计模式更加不是目的。快速的构建客户端功能,并且要让它们清晰、灵活,修改和阅读起来方便,这个才是最终目的。只有代码清晰了,才容易看懂,能够看懂是一切重构和优化的前提,也是改bug的前提。如果我们为了遵循MVC,去硬靠上去,去纠结哪些东西是“显示逻辑”哪些东西是“业务逻辑”,真心不靠谱。
我们现在的客户端框架大概是MVP结构,P(我们叫做controller)承载着绝大多数逻辑和功能,整个客户端就是由一个个的controller组成的。我们为了遵循这个结构,所以明明一个类、一个函数调用就可以搞定的东西,要切分成几个不同的对象,因为这些对象有父子、兄弟、业务、显示这样的关系,所以不能放在一起,对象之间通过一个又一个的事件通知来完成业务流转。我很不看好这样的代码,只能眼不见为净。
我有点不明白,明明类似《我叫MT》这样简单的卡牌游戏都开发了一年时间,而且开发到后面自己都感觉厚重了,这样的MVC有什么存在价值呢?它是适合换皮?还是适合扩展?还是适合删功能?还是适合新人学习?还是适合团队合作?
在我看来,MVC本来就是为软件开发、Web页面开发搞出来的一套东西,最大的功能就是把显示和业务逻辑分离开,这样无论是修改页面内容还是修改后台逻辑都可以互不影响。 而游戏中呢? 界面功能虽然很多,但是是最不用操心的东西了,绝大多数情况是没有任何难度的。 而MVC在非UI部分能够给我们带来多大的便利呢?
我是实用主义,实用主义并不是说代码随便写,能完成策划案就好。实用主义是通过清晰的规则和漂亮的封装,让代码变得清晰简洁,让客户端开发变得有章可循。
大概在一年前,我写过一篇文章,最后有描述我对客户度框架的理解。我很佩服曾经的自己有如此清晰的思路>_<。
“这里也顺便说一下我对游戏客户端各模块依赖关系的看法,说真心话,游戏客户端真的是小项目。那么作为这个小项目,没有必要过分关注耦合,模块化之类的东西。凡是能让我的代码变得整洁,能让我写代码写的顺手方便的功能就是好功能,即便是约束也乐于遵守。但是如果为了模块化,而让我写代码时写个Impl,再写个provider,那会让人恶心透了,事实证明,有些模块化纯粹是杞人忧天。有些复用性的想法纯粹是自作多情,比如这种--你看我的代码模块化的多好,逻辑模块一行代码不用改就可以用到其他项目。我想说,如果你的游戏赚钱了,我们要再做一个类似的项目,那么我就算把你的ui模块也搬过来又有什么问题。如果我们要做的是一个新的项目,那么我还是要从那一坨代码中找到我想要的可以复用的东西,那还不如让代码变得简单,直接些,我更容易理解。
作为游戏客户端,有三个主要模块就足够了,逻辑模块、渲染模块、ui模块,所有跟引擎打交道的地方都停留在渲染模块,渲染模块是对引擎的再封装,即便有少量东西扩散到ui模块也应该只停留在ui控件内部。逻辑模块只负责并且负责完全的逻辑,这也是为什么逻辑层不能引入ui元素的原因。
逻辑层的东西就是一个一个的管理类,负责游戏的各个业务逻辑。 渲染层是对引擎的再封装,比如封装一个人物渲染类,负责渲染人物(逻辑层里还应该有一个人物类来处理业务逻辑,比如姓名、帮派,这个是组合关系)。 ui层就是一个一个的界面。 渲染层封装好后可以几个月不动,逻辑层和ui层一一对应,完成一个业务逻辑就是逻辑层添加一个管理类以及ui层添加几个对话框。他们之间相对独立,有交互就靠上面提到的事件中心来调度。
这样一个事件中心,看着是非常简单的东西,但是却是整个客户端的基础架构,一个好的架构可以让人更加快速的完成功能(而当架构搭建好后,90%的时间都是在写业务逻辑和界面逻辑),而不好的架构可能让我们写代码又慢,bug又多。”
一切的核心是封装,一切的目的是实现客户端的逻辑和功能。基于上面提到的框架,我实在想不到会有什么副作用或者不好的地方。更何况手游项目都不大,如果有人能把一个手游项目写出几十万上百万的代码量,我很难对此表示认同。
三、基于组合编程、面向对象编程、函数式编程?
我们从教科书中学到了面向对象编程,而后面又出了本经典,说应该用组合来替代继承,再后面又有大神出来说函数式编程多么好。
我们应该如何设计客户端的对象呢? 首先函数式编程我是不打算学了,因为我看了几篇文章都没有领会它的精神,但可以肯定的是函数式编程绝对不是面向过程编程,不要对象只写函数就万事大吉的。 这里就要提到一个习惯问题,用自己习惯的写法去做代码封装,而不要用一知半解的概念去东施效颦。
组件式结构必然有其先进和灵活的地方,Unity中的组件思想无论是学起来还是用起来都非常方便,大概是我见过的最漂亮的实现,这个漂亮不光体现在组件本身,还体现在组件间的通信,组件与GameObject的关系等等。
那是不是继承就完全没用了? 当然不是。即便在Unity中也依然会有很多地方需要继承来实现。组件可以描述一个物体的附加能力,而继承则可以描述一个物体是什么,有什么功能。合理的继承可以让代码清晰而精简。 而认为代码不需要继承,只用组合来描述就是高大上的思想,反而会产生凌乱的代码。
四、如何从零开始打造一个手游客户端
原本我对自己缺乏一些自信,认为自己做一个RPG、ARPG什么的缺乏经验和积累,怀疑自己能不能快速的搭建好整个项目。 但是有的时候不一定是自己的成功可以给自己涨自信,别人的失败一样可以给自己涨自信。
1、技术选型。这个很重要。第一步错了,后面就是在错误的道路上渐行渐远。 如果要做2D游戏那就用cocos2d-x,如果要做3D那就用Unity。本来我是不想提这种一刀切的观点的,这样显得很没有水平,因为真正的高手是拿什么都可以做的很好的。 但是很多人就是无法正确的选择客户端引擎,后果就是浪费时间,甚至项目失败。
现在用Unity做的成功的2D游戏也比较多了,所以如果你熟悉Unity的话,那就都用Unity好了,这样可以更好的做团队的技术积累。 而如果没有什么经验的话,那么cocos2d-x+lua是最好的选择,因为简单易上手,招人也容易。 但是要注意,确定了cocos2d-x那就专心做2D的东西,如果美术和策划认为用3D元素才能表现出一个酷炫的游戏的话,那Unity是不二选择。
2、领会选择的引擎的开发流程和开发思路。 比如cocos2d-x中如何处理人物移动、技能播放、UI;Unity中如何处理人物攻击判定、物理效果、组件通信等等。 我刚开始接触Unity的时候花了很长时间去思考这些问题,虽然浪费了一些时间,但是换来的是清晰的思路,当明白了这些问题,才能写出Unity Style的代码。 如果游戏中的基础功能都能够实现出来,那么一个Demo就成型了。
3、游戏最核心的玩法。 大多数情况下就是战斗系统。 这里可能涉及到一些相对困难的东西,比如ARPG中的技能系统设计、三消游戏中的转珠算法、卡牌游戏中的战斗流程设计等等。 这个花一个月甚至更长的时间来做都是合情合理的。 基本上这个东西实现完了,游戏中最重头的部分就结束了,剩下的就是纯逻辑的功能了。而纯逻辑功能虽然耗时间,但是没有什么难度,而且可以靠添人来加快开发进度。
4、数据结构设计。 这个主要是数值策划相关的东西,比如人物有哪些属性,装备有哪些属性,用什么结构来存储人物信息,装备信息存放在哪里。程序的主要工作就是理清楚这些属性该如何配置,如何加载、如何同步。
5、网络通信和UI模块设计。虽然这两个是完全不同的东西,但是解决思路是一样的。选择一个技术方案(比如protobuf,cocostudio),进行合理的封装,使得写这些功能独立而简单。 这个是后面开发的重头内容,客户端一大半代码都是在处理网络消息和ui逻辑。
6、具体功能模块的实现。基于上面提到的客户端架构,“添加一个功能就是添加一个管理类,以及一些列的消息和界面”,我认为这个找个应届生都应该会做。
ex-2:针对第二点我再多说点,就ARPG的技能系统来说,之前我认为要实现一个非常漂亮的技能系统一定要灵活,一定要可扩充,最好策划需要什么新效果都可以自己来组合设计,不需要程序改一行代码。 这个理论上是可行的,但是也仅仅是理论上,我看了一些Unity线上游戏的代码,他们的技能系统并不灵活,有的甚至很死,一个技能就是一个类。 但是这个并不妨碍他们成为出色的ARPG。实现功能是最重要的,只要大的框架有了,细节功能上的设计没必要追求完美。
五、拥抱策划的变化?
首先,我不认为游戏功能不停的推翻重做是靠谱的行为。如果是老板的话,那我还可以接受,但是如果是策划的话,那就真该批判了。我自己常开玩笑说我是专业策划,业余爱好是写代码。我认为策划应该对自己要做什么游戏、游戏有什么噱头、游戏画面和玩法的目标是什么、游戏的最终样子是什么都有一个清醒的认识。如果连最终游戏是什么样子都想象不到的话,那如何保证设计出的游戏会吸引人。 我想那些成功的游戏在设计之初就依然确定了游戏的核心,他们肯定会有这样的认识:这个游戏就该是这个样子的,这样的游戏绝对会火,这么爽的游戏绝对好玩。
其次,即便我们需要应对反复无常的策划,那么我上面提出的框架也一样可以方便的进行修改。 不一定需要严格的功能模块切分,而当我们接手别人维护的代码的时候,更多的时候是理解策划案功能。而认为通过严格的模块划分和模块间事件通信来完成解耦合就可以更加方便的维护整个项目,在我看来是不切实际的。除非真的个人维护个人的,自己的烂摊子自己最熟悉,否则真要查起bug来,说不定更加痛苦。
最后,当我们需要换皮的时候,一样可以方便的搞定。我们很多功能都是独立且清晰的。至少渲染层的封装是绝对可以复用的。而逻辑层很多功能也是相同的,而如果某些功能没有的话,那么对应的ui层也一定不会需要。