组件化开发在游戏开发当中的思考和汇总
由于前端使用了新的unity3d,然而unity3d就是基于组件开发构建起来的。然而服务器这边则是采用了很传统的类抽象模式。一直以为MVC模式可能是一种很好的方式,并没有做很详尽的理解,但是用在unity3d里面感觉并不适用,并且也被明确告知了unity3d并没有很明显的界定MVC。另外客户端原来讨论的时候一度提出过使用脚本,服务器这里任务系统也是用的lua脚本来构建的。很不理解为什么需要这么组织,这种组织的方式有什么优势。即使用脚本了,灵活度得到提升了。最后的理解被答疑为代码的灵活组合,再到热更新的问题。的确脚本化有这个优势,但是这不代表用配表的方式就不能做。后来在去看客户端的对于内存构建的时候,采用和服务器一致的管理模型方式。为什么unity3d要采用这种组件化的方式呢,为什么不类似于cocos那种基于MVC的整体抽象,组件化开发的优势在哪里?这恰恰成为了最近思考的主因,也成为了搜索的目的。
就如同发现新大陆一样的,构建方式!
服务器的组织方式是这种的:
一个典型的,看上去非常不错的继承体系。
仔细梳理出一个链条来看,由于他这是参考的客户端代码所以有Renderable,Animatable等继承关系,暂时忽略掉,如下图:
可能哪一天吧,一个已经存在的对象不能移动了,再或者装备需要新的属性特性来做扩充,道具的种类扩容限制条件增加,再或者由于原来的类命名不合适,做继承的时候造成了代码的“混乱”语义混淆,而这些都是可能遇到的需求问题。我们所能够做的就是重构代码块,调整继承树关系,再或者暴力一点的没设计好砍掉这个功能。而这个继承树就成为了头重脚轻的代表了。
在看下图,不用is-a的继承,而是面向接口基于has-a的组合方式来构建对象实例的话,有一些需求变动就不存在问题。如果功能获得扩充add就可以了。现在项目的任务系统给了我一些启发,任务系统的构建具有很高的独立性,通过脚本化的组合来实现不同任务的需求,适合游戏开发中经常反复的过程。那么延伸出来游戏的实体对象也类似,或者更激进一些对象需要什么行为完全交给策划自己去组合一个新的对象出来。一个完全由数据驱动的系统,而摆脱限制。对象不具有任何意义,只是按照会飞,会走的,会放技能的惊醒的对象管理和划分。类在做抽象的时候,可能就简单到对应一个策划表的一些基本属性就够了。而抛开了纷繁的方法定义在函数内。
参考博客的一篇评价:
本文描述的基于组件的对象系统,适合在游戏开发中经常反复的过程。由于对象没有固定的类型概念,所有的对象都是动态地由组件组合而成,而这些组件都统一由一个管理器来进行约束。相比传统的基于继承的方法,这种方法带来三大优势:一是方便创建和修改复杂的类型,由于不再需要改动庞大的继承树,绕开了语言的静态限制,客户端可以在不修改代码的前提下,创建任意类型的对象。二是由于组件是对于接口设计的,这就强迫设计者实现一些高内聚低耦合的组件,也有助于游戏层的整体设计。最后由于可以动态地查询组件类型信息,做一个拥有图形界面的、支持游戏内容的debugger变得可能了——摆弄对象的企划可以实时查阅对象的能力、状况,在传统方式下要实现类似的功能恐怕是相当繁琐的。有得必有失,基于组件的方法由于必须实现一个基类,定义虚接口,在某些无需vtable的简单情况下造成了一定的性能开销。此外,每次查询接口引起的开销也值得引起重视。
类比于cocos MVC的抽象,早起cocos2d是基于OC语言的,那个时候的一篇博文,倒是也说明了一些问题。这个问题,一下为原文:
当我们在cocos2d论坛里面提到“是否继承CCSprite还是使用一些model类来构建你的游戏对象结构?”这样的问题的时候,我还是要再强调一点,多用组合,少用继承!如果一味地使用继承,那么当游戏世界里面的对象种类变多,功能变复杂以后,会导致整个继承树“头重脚轻”,严重破坏了良好的面向对象设计原则---我们设计的类层次结构应该是扁平结构的,而不是一个头很大的树。
那么,我们需要使用怎样的架构来处理游戏里面的对象呢?答案就是使用组合。现在已经有一些非常好的引擎,比如TorqueX、PushButton Engine、Unity3D等,它们都是基于组合的实体组件系统。
自我思考:
现在服务器尽量采用了单一职责,一个模块,一个类,一个manager,一个文件做一一对应的逻辑划分。很好的避免了头重脚轻的方式。这也是开始对于背包和装备装上和装下理解的不足,从而先做出来了一个背包基类的概念。显然违背了一个基本的设计原则就是扁平化。紧跟着延伸到数据库存储的设计就已经不合适了。
那么服务器能不能采用,这种基于组合方式的代码组织方式呢?由于没有见到过类似的服务器,也不知道别人有没有这么做的,因为一个新的东西肯定还是有很多问题需要解决的,可能丢了西瓜捡了一个芝麻也说不定呢?现在能想到的优势就是,由于组件化了,单元测试成为了可能,在最终服务器做逻辑拼合的时候,其实已经零碎到组合的小函数上了,至少在添加新的模块的时候或者项目过于庞大的时候,一种基于单元测试的全回归能提前排查出来很多问题避免改东边落下西边的问题,单元测试也使得服务器也可以适度的抛开客户端连接调试才能查错的问题。但是劣势也是明显的,如果对于游戏本身了解不是很深入,可能在定义高内聚低耦合的函数接口的时候就已经抽象不出来了因为不知道什么函数具有公共特性适于组合。
参考博客:
1.http://job.17173.com/content/2009-08-07/20090807104220649,1.shtml
《一个基于组件的动态对象系统》
2.http://www.cnblogs.com/zilongshanren/archive/2012/03/11/2390074.html
《cocos2d里面如何实现MVC》系列