提起游戏引擎,特别是商业通用游戏引擎,比如 Unreal 或是 Unity ,给人的第一印象会是它们的可视化编辑器。而在实际开发中,在不同引擎下做游戏开发,影响最大的是引擎层的 API 以及这些 API 带来的模式。
而对于使用自家专有引擎开发出来的游戏,却少见有特别的编辑器。比如以 Mod 丰富见长的 P 社游戏,新系列都使用一个名叫 Clausewitz 的引擎,玩家们在之上创造了大量的 Mod ,却不见有特别的编辑器。Mod 作者多在文本上工作,在游戏本身中调试。游戏程序本身就充当了编辑器:或许只比游戏功能多一个控制台而已。在这类引擎上开发,工作模式还是基于命令行。
游戏引擎中的那个编辑器无疑是引擎开发中耗工时最多的部分。我们自己研发游戏游戏已有 5 年 ,其中编辑器完全重做了两次,目前第二个人维护着第三版。在这几年里,我一直在思考:游戏引擎到底需要一个怎样的编辑器、它应该用来解决怎样的问题。
在 20 多年前,我写风魂的时候,它受 Allegro 的影响最多。当时,只要封装出 API ,解决图形、声音、键盘、鼠标、系统窗口等的底层调用就解决了游戏开发中最难的部分。后来,到 2001 年开发大话西游,我根据游戏的需求为引擎写了几个小工具,用来编辑场景、2D 动画等等,支撑了游戏开发。那些工具是为游戏定制的,同样定制的还有一些对应的程序模块。我认为不属于引擎的范畴。
后来,各大游戏公司纷纷转向了商业游戏引擎,家酿引擎越来越少了。我在 2005 年时开发 3D 引擎时,也受那些商业引擎的影响,觉得游戏引擎必须要有一个大而全的编辑器。如果缺少这个,似乎没有人愿意用它开发游戏。2017 年底,我重启游戏引擎计划时,依旧觉得,开发一个编辑器非常重要,否则游戏引擎很难吸引开发者。
但是,别的引擎有什么,我们就应该做什么。这绝对不是一个好主意。因为复制似是而非的特性并不能真正解决问题。我们首先需要理解问题。一个功能丰富的游戏引擎编辑器看起来是为了减少编码 (low code ?) ,降低游戏开发的难度,让不太会写程序的人充分发挥他们的创意。
但这并不符合我们自己的需求。因为,我们项目组的所有人都有丰富的编程经验,code 并不是难事,不用会、少用 code 做同样的事反而增加了开发难度。对于一个软件项目来说,开发者必须是第一用户,Dogfooding (吃自己的狗粮) 对软件开发尤为重要。在软件开发这么多年中,我学到的最重要的一点就是:如果一个特性你不常用到,那么就应该立刻从代码中删除,直到以后用到了再加回来。所以,我们编辑器的第一次完全重构就是因为抛弃了复刻一个 Unity 编辑器的想法。我们做引擎绝对不能因为别人有什么而做什么,用户也不会因为这个引擎也有同样的功能而选择它。如果我们自己开发游戏不会用 low code 的模式开发,那么我们就不应该做一个以减少编码为目的的编辑器。
那么,是不是意味着我们的游戏引擎不需要一个丰富的可视化编辑器?只需要把 API 设计好,可以方便的用代码构建游戏就够了?
有一段时间,我们在引擎开发上是这样的:用简单的几十行代码就可以搭出一个小 demo ,测试或展示某个特性。但这会让引擎停留在渲染层上,离做游戏还很远。最糟糕的是,这些 demo 代码中充斥着 magic number :摄像机的角度、灯光的参数、硬编码的文件名…… 不可忽视的是:游戏中大量的内容是以数据形式表达的,而不是代码。数据最终呈现的是画面效果,它们需要根据视觉效果调整。一个可以快速启动的程序能够改善调整这些数据的体验;但通过文本编辑器修改这些数值绝不是高效的方法。所以、我们需要可视化编辑器。
游戏引擎的编辑器:是一种用来产生游戏数据的可视化工具。这些数据如果可以用更成熟的工具产生,那么就不必将功能集成在编辑器中。例如,我们并不需要引擎的编辑器做 3d 建模,也不需要有笔刷像 photoshop 那样绘制贴图;同样,集成一个代码编辑器编辑脚本的意义也不大。它最重要的作用在于把代码逻辑和数据分离。一个好的编辑器可以产生出数据,然后引擎的代码只需要读入这些数据就可以创建出游戏中的实体。
同时,引擎的 API 应该为之简化。在缺乏编辑器时,引擎 API 层提供的大量 API 都是用来让代码可以正确的构造游戏实体。这些 API 反应了各种数据是如何控制每个细节的。但有了编辑器创作好的数据后,数据已经代码运行之前就组织成应有的复杂结构,所以,引擎只需要提供单个 API 加载这些数据就够了。所谓编辑器,编辑器的就是某种预制件(prefab),预制了最终运行时的数据结构。游戏的大部分数据是用于视觉表现的,所以需要一个可视化的手段编辑和呈现。
编辑器产生的数据是引擎运行时的输入。这些数据应该是易读的,但不必是易于(用文本编辑器)编辑的。我们设计了一个专有格式 来描述游戏引擎中的大部分数据。尽量把代码逻辑(用 lua 编写)和数据分离。引擎提供的 API 中很少有特别细节的控制接口,所以,脱离编辑器制作一个游戏 demo 是很难的。因为开发者缺乏直接控制构建数据的 API ,难以硬编码摄像机位置、灯光信息、角色的空间状态在代码里,最佳的途径是加载一个编辑器编辑出来的预制件。这样,也促进了我们吃自己的狗粮。避免编辑器成为游戏引擎的一个边缘子项目。当然,因为编辑器产生的数据还是文本的,硬去手写一个预制件也不算太困难。
在搭建了最终的编辑器框架后,我们更多的是在实际游戏项目开发中遇到问题,就顺手给编辑器加一点功能。例如,编辑一个摄像机运镜的轨迹、写太阳一天的日夜循环模拟,方便美术调整光照变化的参数,等等。这些功能都是为实际游戏服务的,并没有打算做成通用引擎的一部分。
鉴于我们现在开发的游戏中并没有使用物理特性,前两个月便把曾经集成好的物理模块又去掉了。同时去掉的当然还有相应的编辑器功能。而预制件的动画及特效的时间轴编辑则在不断完善,因为美术总有一些需求,原来是在外部工具(例如 blender )中编辑好再想办法把数据导入引擎,慢慢的却发现在我们自己的编辑器中编辑有时更方便。只维护现在游戏项目用得到的特性,我想这才是一个好的状态。