写下这个题目时我是为自己捏了把汗的,因为我极不愿意给自己写的程序冠上“框架”的名号。我更愿意以“模块”或“库”之类的名义开发一个东西。这并不是文字游戏,框架两个字拆开看的话,“框”是limit,是限制性的约束,“架”是support,意味着支撑,在我看来框架在提供了一定的功能同时限制了使用者:你要用它,就要按照我的规矩来,可能包括但不限于流程、逻辑对象交互机制、扩展手段、强制性的特殊命名等。这也就是为什么我更愿意去开发有更好的自由度的module或library而不是framework作为可复用单元。
只要留心总能在各处找到和游戏engine、gameplay相关的大量资料,游戏tool相关的却很少,这篇文章我想如果它只是为引玉而抛出去的砖,也保证它的质量和含金量,是你捡回去能砌墙用上的砖。这次要谈的想法成型已久,但最近项目比较清闲我才有空把它实现出来,实践过后就分享出来吧。框架是基于 DotNET构建的,我曾经在《游戏工具四层结构》中说过这种架构的最初想法,基本结构没有变,但是一些模块耦合方式略有修订。我曾经认为有了层次划分清晰良好的结构用什么语言去实现它都是等价可行的,现在更进一步,虽然用单纯的C/C++这类死板静态的语言也能实现但一定要比结合使用包括一些更动态的语言(如C#)来得繁琐和耗时。最近听一个同事给我讲解了一下Unreal引擎中Unreal Script定义对象,生成相关C++代码,利用反射衔接到编辑器中这一套机制,我想,这套用Native C++实现的方案正是用DotNET就能更容易的等价解决的,想做灵活的编辑器一些运行时的动态特性是无法避开的;使用DotNET的另一个好处是做界面方便,在VS中直接WYSIWYG,剩下的工作就是配套自定义一些时间轴、Native到DotNET的类型信息反射绑定系统。
我遇到的大多数游戏程序员都只擅长于或专用Native C++,在团队里推行DotNET的一些东西要有能帮不懂它的同事快速上手的执行力和十足把握,最近看了《Scala很难》一文很有同感,类似文中表达:
当你的团队有相当的人数,你试图教会这些Native C++程序员使用DotNET,而他们又非真心的想学时,这成了相当讨厌的事。如果你的团队的技术水平很一般,多语言集成开发也许对你们公司来说并不是一个好的选项,DotNET很难。并不是它本身很难,而是因为它在水平一般的团队中不会产生那种由技术很好的人组成的团队中产生的短期或长期的益处。多语言的难度导致很陡的学习曲线,会遭到原有的程序员的反对,形成不了统一的风格。你需要一个强有力的CTO或架构师来强迫这种风格,而不是让他们自己从书中学习。
(对于上面一段话请勿反感,我把别人的原文拿过来把Scala、Java之类的改成了Native C++、DotNET等,尽量尊重了原文,我从来没有认为会的语言少=水平一般,我猜原作者也没有这个意思。言者无意,闻之自辨。)
2009年春天我在盘龙OL项目中,我曾主张游戏工具在开发语言上使用Native C++ & CLI C++/C#的两层结构,类似下图:
当时同事说CLI C++和Native C++都是C++,C#和C++是不同语言差异太大不好学,并且层次多了太麻烦,最终使用了一种Native C++ & CLI C++搅在一起的单层结构,如下:
那之后我参与了两年多的PL项目工具开发和维护,自己也一直为完善编辑器开发的良好方式做尝试。我总结当初PL工具开发方式的优缺点如下。
优点:
(1) CLI C++能使用DotNET开发的特性,如WYSIWYG的界面布局,丰富的DotNET类库辅助;
(2) CLI C++能无缝集成Native C++写的引擎底层。
缺点:
(1) CLI C++细节繁多,不仅避免不了DotNET的关键概念,还要接受其另类的语法风格,总体上Native C++程序员学习CLI C++要比C#麻烦;
(2) 在VS中拖控件自动生成的代码是放在Form的。h文件里的,要手工把函数的定义从声明处分离开放到。cpp中;
(3) 当一个Form的。h中有大量控件信息时IDE经常出现假死、无法正常显示和操作、生成重复事件处理函数错误、生成数G到数十G的临时文件到硬盘等诡异现象;
(4) 层次不清,代码组织容易混乱,容易滋生异味代码。
另外游戏工具开发应极力避免设计开发过度分散,没有一个总的规划和把控者。各自为政会成为软件工程混乱的开端。相对掌握新的编程语言而言,为特定的需求选定恰当的语言并向不熟悉它的同事讲解更重要和有难度。现在我想在有充足的理由说明某个技术方案的确适合项目和团队成员时,我会不遗余力的主张的。
此篇框架的设计开发有如下几个目的原则:
(1) 只作为游戏工具的通用流程性解决方案,与具体引擎无关;
(2) 操作的触发和执行分离,应用开发者只关心界面或引擎;
(3) 模块和层次合理,界面和引擎层之间的部分属于黑盒,对应用开发者半透明。
C#的确有直接调用Native DLL中C接口的能力,但我认为CLI C++写的这一层仍是必要的,下文会详述理由。C#和CLI C++的接口均可方便的暴露给IronPython使用,脚本的加入一是为了充当配置文件二是给工具提供更灵活的控制和调试手段。