宇宙无限 发表于 2021-2-22 09:11

游戏引擎随笔 0x00:脚本

人年纪一大,就容易怀念往事。回想14年前,第一次接触 OGRE,恍如隔世。一入引擎深似海,从那时起,引擎梦一直萦绕在我心头,始终挥之不去。这十几年间开发过几代自研引擎,也使用过许多商业\开源引擎,从这里开始,我打算写写这么多年来积攒的游戏引擎开发相关的东西,不见得有多么全面和正确的知识,更多的是个人的感悟和理解。也不知能写多久,希望能尽量二周一更,或者至少每月一更。
上古时代的游戏引擎出于性能考虑,几乎都是使用 C++来写游戏逻辑,C++优势明显,缺陷也很明显,极高的学习成本,对使用者提出了超高的要求,没有3、4年以上的实战开发历练,很难能把控的很好,漫长的学习曲线和极高的调试代价,开发成本越来越难以接受。新世纪以来,硬件算力有很大提升,游戏也在不断的进化发展,对应功能的开发难度不断提升,同时新的硬件平台和操作系统又在维度上增加更多的复杂性。尽管C++语言本身的发展(C++11、C++14、C++17 以及即将到来的 C++20),在一定程度上有所缓解,但仍远远跟不上游戏市场需求发展的速度,在这种情况下,迫切需要开发效率更高的游戏程序设计语言,因此,脚本语言逐渐发挥作用,直到目前已经成为主流的开发选择。
记得 2003 年我用 C++ 写了个 RPG 游戏框架,用 Python 脚本做剧情角色对话,开发了一个微型 RPG Demo,由此敲开了游戏行业的大门。之后几年一直用 C++ 开发游戏逻辑,一次偶然的机会我和一位年近 50,正在开发星战 MMO 的美国程序员交流,他告诉我说他们正在开发的 MMO 服务器端逻辑使用 Python,客户端使用 Lua 来开发游戏逻辑,而且是绝大部分的游戏逻辑。当时我很兴奋,原来在商业上用脚本来开发游戏逻辑是真的行得通的,也许就在那时,使用脚本来开发游戏逻辑成为我的执念。
自从 2006 年开发游戏引擎开始,我意识到需要为引擎引入脚本系统。那时国内比较流行的脚本语言是 Lua,一方面由于是成功的运用在商业游戏项目中(大话西游、梦幻西游等),并且当时国内的技术领袖 云风 也在大力倡导,另一方面是《魔兽世界》使用 Lua 做为 UI 自定义扩展方案。于是在自研引擎投入商用的第一个项目中,我也给开发团队引入了 Lua。但也许是时机还不够成熟,项目团队仅仅是将 Lua 做为数据存储方案来使用(Lua table 表示数据确实方便),几乎全部的游戏逻辑还是写在 C++ 中,所以并没有充分发挥脚本语言的优势。就在那时,我接触到了 UE3,先进的可视化开发方式(可视化关卡逻辑编辑器 Kismet 、可视化材质编辑器、可视化粒子编辑器等)给我很大的震撼,尽管那时 Kismet 还只能做关卡逻辑,无法针对游戏对象编写逻辑,但这种新的开发理念和思想依然给我带来巨大的冲击,所以在后来公司的第二代引擎研发中,我开始研发可视化编程系统(Visual Programming,以下简称 VP)。但是很可惜,刚做了个 VP 编辑器的雏形,由于种种原因,引擎项目无法进行下去,只好黯然离开去了腾讯。
2012 年腾讯收购了 Epic 50% 以上的股份,我也近水楼台先得月,开始做些 UE4 的预研工作。当看到 UE4 抛弃了 Unreal Script(实际上也没有完全抛弃,Script VM 还在,并且作为 蓝图 系统的 运行基石),只给开发者提供两种选择:1、C++ 2、蓝图。UE4 将 蓝图 看作为 脚本系统,这个脚本系统是建立在引擎的最核心基础对象: UObject 之上,它包括了反射、GC、脚本虚拟机、蓝图元数据等等,为了将 C++ 对象信息暴露给 蓝图,UE 还自定义了一套 UBT\UHT 工具,不仅如此,还需在定义 C++ 对象时必须以侵入式方式加上宏定义代码,这样 UBT\UHT 方可识别。UE4 给开发者两个极端的选择,过于复杂和过于简化,缺乏中间脚本层次的过渡。这带来两点问题,一是C++的高门槛提升了开发难度,二是蓝图对于有传统开发经验的程序员不容易接受。蓝图是更上层的控制流程,当复杂逻辑或者性能敏感时还是需要依赖 C++ 提供相应的支持,但这又受到第一个问题的制约。尽管 Tim Sweeney 解释了为何使用 C++ 放弃了 Unreal Script,但这些理由作为放弃 Unreal Script 很充分,但对于使用纯 C++开发却并不足够。为了在实际的开发中解决这些问题,于是催生了加在 C++ 和 蓝图 之间的各种脚本解决方案,如 slua-unreal、MonoUE、USharp、UnrealEnginePython 等。无论上述哪种脚本方案,都面临一个麻烦的问题:双 GC。UE4 中 C++和蓝图共用同一个 GC,而脚本又有其自身的 GC,当这两者同时运行时,很难避免 GC 不一致导致的各种内存问题。当然可以从架构设计上尽量规避,又增加了限制和难度,一定程度上也违背了使用脚本的初衷。再看 Unity,以 C# 做为游戏开发脚本,C++ 引擎层提供脚本访问接口,几乎所有游戏逻辑都运行在 C# 中,由此可见,单单在语言层面,Unity 的上手难度上就要比 UE4 低的多。Unity 也有 VP 的解决方案,都以插件形式存在(Bolt、PlayMaker、NodeCanvas、FlowCanvas 等,官方的 VP 功能尚处于开发当中)。Unity 的这种方式就完全涵盖了游戏编程领域里的高(可视化)、中(脚本)、低(C++)三个层次,可以让开发者根据应用场景来选择。
如果重新设计一个新的游戏引擎,我会使用成熟的脚本语言作为主要逻辑开发方式,这样引擎 Runtime 层可以做的很薄,只包含必备的基础功能和核心对象,通过脚本系统的绑定机制将 Runtime 的对象、API 等导出给脚本使用。GamePlay 相关的代码几乎都可以移到脚本层中,这样 Runtime 甚至都不需要提供反射和垃圾回收功能,因为几乎所有脚本系统都有这两个功能,利用脚本的反射进行动态识别,利用脚本的 GC 实现对象的释放。对于 Runtime 的对象,比如资源等,Runtime 层可以实现自己的一套 GC 机制,这样不受脚本 GC 的限制,Runtime GC 和 脚本 GC 互不影响,彼此独立。对于使用 C++开发的逻辑对象来说,编写就不必像 UE 那样需要侵入式宏代码,更加简洁、纯粹,降低开发者的学习难度。如果需要 VP,也不必在 Runtime 层提供支持,可以作为插件形式存在。Runtime 层已经将接口暴露给脚本,如果游戏逻辑对象也是脚本编写,那么对于 VP 来说直接访问就可以了。一个需要额外的考虑因素是逻辑的热更,VP 作为资源存在时是可以热更,但由于最后生成的是逻辑描述而非脚本代码,所以性能必然会有所影响,不过可以通过 VP 生成脚本代码,甚至是 C++代码(比如 UE4 蓝图生成 C++ )来解决性能问题,不过这对于某些平台比如 iOS 就失去了热更的特性,当然这也可以通过设计来规避,比如性能敏感的代码放在脚本或 C++ 中实现等等。
如何选择脚本语言是极其重要的问题,这将影响着引擎的 Runtime、编辑器、工具链、开发模式、还有引擎的开发生态等等。首先排除自己发明脚本的想法,如 Unreal Script 发展了十几年,最终还是被自家抛弃,具体原因不再赘述。其次排除的是多脚本支持,如 Unity 曾支持多达 3 种脚本,但最后只支持 C# 一种脚本,对于这样的选择官方也给出了足够的理由。再次,排除 C++ 或魔改 C++ 做脚本的方案,具体的原因上面也详细阐述了。近些年,程序语言依然在不断发展,老牌的如 Java、C#、Lua、Python、JavaScript 等等,新的有 Golang、Rust、Kotlin、Julia 等等,该如何在这些语言中做出选择?在我看来,需要重点考量的有以下几点:
性能,毋庸置疑,作为首要考虑的因素。跨平台,支持主流的游戏运行平台。成熟度,广泛应用在商业化项目中,达到工业级水准。普适性,拥有广泛的使用用户。学习曲线,上手简单,学习成本低。可编译,或 Native 化,在编译器可检查出大部分错误,提高开发效率。先进性,元编程、泛型、多线程、并行、正则、优秀的 GC、内存管理性能等等。可持续发展,要有足够强力的机构或公司维护和不断改进。丰富的第三方库。IDE,方便的调试工具。
根据上述的条件过滤,可以在众多候选者当中选择出合适的脚本语言。
关于脚本目前能说的就这么多,这些只是粗略的谈谈,其实每个点都可以扩展出很多内容,希望以后能有机会再单独开篇再聊。

风吹吹蛋蛋疼风w 发表于 2021-2-22 09:12

所以作者倾向哪种语言呢?

掌舵的鱼1987 发表于 2021-2-22 09:14

感觉你应该可以去玩玩Xenko了,可惜这引擎目前维护者太少。

万胜 发表于 2021-2-22 09:16

我几乎已经给出答案了[机智]

123456811 发表于 2021-2-22 09:21

嗯,这个引擎从五年前就开始关注了,也是个相当不错的研究对象

飘渺九月 发表于 2021-2-22 09:28

我猜是Lua,是吗

形腿望舞 发表于 2021-2-22 09:35

如文中所述,lua 我用了十几年,非常熟也很有感情,但却不太符合面向未来的游戏引擎的选择脚本的那几个标准。

123456825 发表于 2021-2-22 09:38

按作者列的标准,基本只有C#符合标准。

普通人物怨 发表于 2021-2-22 09:41

现在只爱js

胡37 发表于 2021-2-22 09:50

跨平台几乎筛选了所有
页: [1] 2
查看完整版本: 游戏引擎随笔 0x00:脚本