参与了现场交流的同学可能会发现,这篇文字版跟现场版相比,虽然幻灯片保持原状,但文字注解部分略有出入 (增加的内容主要是第二部分“评估,运用和改造”,这一部分在现场时因为时间关系说得非常简略),还请见谅。 内容提纲
部分:游戏引擎的十年变迁 (2006-2016) 在这次分享中,我着重对比了个人相对有所了解的五款商业引擎,分别是 Unity、Unreal、CryEngine、Gamebryo 和 Torque。而下面划掉的开源引擎 OGRE/Irrlicht/cocos2d-x 性质不同,放在一起对比并不公平,所以排除在这一回讨论的范围以外。 前几个 (Unity、Unreal、CryEngine) 比较知名,就不多做介绍了。这里简单介绍一下 Gamebryo 和 Torque。 Gamebryo 是当年 (2007-2010) 国内非常流行的商业引擎之一,业内不少大厂都曾拿这个引擎做过各类项目,也培养出一批熟悉 Gamebryo 的引擎程序员 (包括我在内——虽然我是经由 Unreal 2 启蒙,但只有在 Gamebryo 上的工作才让我看到了自己曾尝试着去实现的理想中引擎的影子)。 Gamebryo 的强之处在于其设计上的普适性——与 Unreal 和 CryEngine 内含的 FPS 基因不同,Gamebryo 对于 MMO、赛车、休闲、动作等国内常见的游戏类型均能很好地适配。从基于 Gamebryo 的两大代表作《文明4》和《辐射3》在游戏类型上的跨度,就可以看出这种不同寻常的通用性。它的整体架构简洁有力,模块边界符合直觉,模块之间耦合性低,模块实现内聚性强。这些特性使得它的学习曲线相对平缓,新人容易培训和培养,因而也是我个人偏爱的一款引擎。Gamebryo 的弱点在于自身工具链不足,且与第三方的集成度偏低,以及由于前两者导致的引擎提升潜力受限。 而 Torque 则是前些年的低成本商业引擎的代表,其特色在于简单易用的编辑器和独具特色的脚本,基本上可以看作是 2010 年以前的 “史前版 Unity”,前述两大特色也被后来者 Unity 继承并发扬光大。 在这张基本时间线的图上,我们可以看到在过去十年里这五款引擎大致的发展轨迹。其中可以看到,贯穿始末的是 Unreal 和 CryEngine,而另外三款引擎则流行于特定的历史时期。而即使是 Unreal 和 CryEngine,在漫长的发展过程中也自有其高峰和低谷。 有了它们各自的时间线打底,我把这五个引擎各自比较有影响力的版本标注了上来,这样在以五年为跨度的单位 (垂直虚线) 上,每个引擎曾做过的大动作就一目了然了。有过相应项目经历的程序员,对这些曾经活跃一时的版本一定不会陌生。这里我们先不针对单独的版本一一细说,只是对每个引擎的大致活动情况了解一个大概。 细心的同学也许已经注意到了,看起来很凑巧的是,这些引擎的大版本或多或少地聚集在我标出的以五年跨度为单位的竖直虚线附近。而这其中为显著的就是 Unreal。这仅仅是一个巧合么,还是说,跟其他的时期相比,这些特定的年份有什么不同? 站在 2016 往回看,我们意识到——在这十年中,这是仅有的两次有着整体产业级影响力的根本性的变迁和进化。如果说是两次技术革命 (Revolution) 可能不甚恰当,但要说这是两次影响范围甚广,使得游戏产业的深度和方向产生了根本性的延展的两次行业范围的演化 (Evolution),似乎并不为过。 其中次演化,是起源于 2002 年并于四年后 (约 2006 年前后) 渗透到整个行业的,被广泛应用于游戏引擎中的可编程图形流水线 (Programmable Graphics Pipeline);而第二次演化,则是起源于 2007 年的初代 iPhone 并在四年后 (约 2011 年前后) 渗透到整个行业的,改变了整个游戏行业生态的移动平台游戏开发 (Mobile Development)。 可编程流水线刚刚被引入到实时图形渲染的领域中时,是 3D 图形处理器发展迅速的时期,从 Geforce 3 开始,nVidia 在硬件方面每半年就有一次较大的更新,同时期的各类图形技术一时间层出不穷,可谓是画面渲染质量提高快的黄金时代。在2006年时,我任职于育碧上海,有幸参与了 “细胞分裂:双面间谍” 的开发,把当时的游戏截图 (注意后一张是游戏中上海关卡的东方明珠夜景图) 拿到现在来看都并不过时。得益于复杂度迅速提高的材质和各种开脑洞的特效实现的不断涌现,整个行业在图形和渲染表现方面的探索非常迅速和深入,通过真实感图形渲染出来的游戏画面达到前所未有的逼真程度。 而移动平台的游戏开发则是另一次重要的变迁。在新的平台上,随着大量非传统核心类玩家的涌入,人们有着与传统 PC/Console 迥异的关注点,从单一的超高画面和沉浸感追求拓展为更多元的交互和体验。可以说直到如今,我们仍处于这次演化的余波之中。 我们注意到,这两次演化的共同点在于:它们皆非短时间内剧烈的革命,而都是跨度若干年,并于图中所标示的年份上遍及整个行业的产业级演化。而不同之处是:前一次的演化,在图形领域内前所未有地挖掘了电子游戏作为可视化交互艺术可能触及的“深度”,而后一次则通过全新的平台和交互语言大大拓宽了行业所能触及的“广度”。 回到时间线中,我们可以发现,Unreal 和 Unity 对两次行业的演化有着强烈的预期和精准的判断——在这些决定命运的转折点上,他们要么在巨变来临前就做好了充分的准备,在行业变迁中始终屹立不倒,要么凭借着顺应趋势的特性集和服务乘风而起。 现在我们重点来看一看 Gamebryo 、CryEngine 和 Turque,它们是在次行业演化中崛起的佼佼者,在各自的量级上均是罕有敌手。Gamebryo 的标准材质 (NiStandardMaterial) 对应着可编程流水线在现代商业引擎中简易和灵活的实现,是那个年代 (2006) 容易上手的商业引擎之一;CryEngine 的图形表现在孤岛危机时代是强的引擎之一 (这里的两个“之一”二字仅仅是意思一下);而 Torque 更是为小型独立开发者提供了极易上手的编辑器和非常丰富的着色器开发套件 (基本上可以认为他们自己弄了个内置的低配版 Asset Store)。所有这些让它们在次行业演化中脱颖而出,在devmaster.net (这是那个年代的一个专注游戏引擎和中间件目录的开发网站) 上成为提及率高的几个商业引擎。 然而时过境迁,在推出了若干有影响力的版本之后,它们不约而同地在 2011 年前后平静了下来。很明显的是,它们并没有意识到时代的趋势,在移动平台崛起时,它们没有做出任何反应,反而把精力用在了事后看起来无关紧要的改进上。 Gamebryo 的用户希望引擎能提供更好的编辑器和工具链,而 Gamebryo 前后尽力做出的两版编辑器虽然看起来很不错但并未达到工业级的成熟度 (很像后期的 cocos2d-x) ——在拥有成功项目的支撑和回馈之前就陷入了兼容性的泥潭。而 CryEngine 似乎落入了“为了强大而强大”的漩涡,并未意识到在图形技术日趋成熟的时代,由于边际效应递减,图形上的优势越来越难以形成差异化。与老对手 Unreal 为 iOS 专门打造的瘦身版 UDK 截然相反的是,CryTek 精心打造的“新版” (Rebranded) CryEngine 甚至似乎刻意在逃避移动平台——它的主要特性是支持 Linux 和下一代主机 (PS4 / XBox One / Wii U)。 Torque 则是这三者中为惋惜的。按照现在的眼光看,它是商业引擎中便宜的 (~$200),它的目标群体是小型独立开发者和团体,它的编辑器像 Unity Editor 那样亲切好用,它的脚本 TorqueScript 神似 C#,它甚至有一个 Torque 3D Store (可以看做是 Asset Store 的前身,现在仍然可以访问)。在 StackOverflow 上,你甚至能看到 (2009 年) 这样一个有趣的比较:“” 但是,由于对移动平台的无视,Torque 的用户源源不断地流向了 UDK 和 Unity。 Gamebryo 、CryEngine 和 Turque是三款定位迥异的游戏引擎,却有着极为相似的起落周期——在次演化中提供了各自领域有价值的服务而崛起,和对第二次演化的无视甚至逃避导致的衰落。这三款引擎所有的有影响力的版本均在我们框定的次演化和第二次演化之间。在移动平台普及之后,这三款引擎再也没有推出一个有影响力的版本。 我们拉近视角,近距离观察 Unreal 这个贯穿十年,历经波折却仍然稳步前行的游戏引擎,看看它的步调和动作是如何与时代趋势相匹配的。在图中我们不难看出,Unreal 总是能够捕捉并提前准备好对应的发布——细究每一条细节,Epic Games 在十年里完整地向我们诠释了“与时俱进”的真义——在行业需要深度的时候提供足够的深度,在行业需要广度的时候提供足够的广度。(呃,一不留神成了 Epic 吹了~~) 接下来是部分的结论——不是强的,也不是新酷的,更不是贵的,而是适应变化的,活了下来。这个结论是由达尔文的进化论金句略作修改而来,原文见下面的方框。 (部分完) 第二部分:一次技术迭代周期 如果想要各种姿势自顶向下自底向上巨细无遗地了解游戏引擎的方方面面,可以看 Milo 老师的,这里就不多说了。我们抓一下挈领,把游戏引擎的评估放在重要的位置,试着解决一下关于 “How” 的几个问题——“How to evaluate” (评估)、“How to use” (运用)、“How to extend” (改造)。 游戏引擎的评估 此前我曾写过一篇文章:2014-07-28 这篇文章里我集中讨论了评估中间件需要注意的一些情况,在文末我写道, “对中小规模的技术而言,上面的“望,闻,问,切”已经足以应付了。而对大型代码库/框架/引擎而言,又有一套不大一样的评估标准,另有曲径可探,咱们择日另行讨论,此处暂且按下不表。” 当时给自己挖了个不大不小的坑。两年多过去,现在这个坑终于可以填上了。 关于引擎评估,首当其冲的是三个简短的问题,我们一个一个来看。 首先,“是否经受过同类产品的考验”是一个决定性的因素,这不仅仅意味着能否按时交付,技术风险高低——更多时候,这是你的团队不会因为计划外因素而意外搁浅的重要保障。我们知道,捡到一个存折带来的喜悦远远低于丢失一个等额的存折带来的痛苦,同样的,在幻想你的游戏大卖之前,先尽一切可能确保你的项目不会因为无法控制的因素夭折显然更有意义。没有经过考验的引擎就像是一杆没上过战场真刀真枪考验过的枪,在它有效地为你杀敌之前,先祈祷它不会伤到自己吧。这也是市场上看到的山寨产品 (对国产游戏而言) 和续作 (对 3A 游戏而言) 这么多的直接原因。 其次,“好招人吗”。这个问题表面上看是一个团队管理和人员招聘问题,而实际上却是一个学习曲线和培养成本的问题。在 Unreal 推出 UDK 之前,很多用 Unreal 引擎的小团队在快速出了原型之后都难以为继,这是因为那时的 Unreal 技术人员的培养成本很高,培养时间很长,在人员快速扩充时期,小团队很难消化新手和准新手给团队带来的负担。这就造成了巨大的反差:两三个高手可以拿着 Unreal 在两个月内迅速出一个华丽的原型,在期望值迅速提高之后,弄来一大票人吭哧了一年多,在项目节点上老板一看,哎哟我去,这可不还是那个原型吗~~等等,好像还不如刚开始那时候稳定了~ 后,“是否有代码”。这个问题在文章里已有表述,这里引用一下: 后说一下这里面一条俺认为比较重要的,也是当年带队的MMO项目里,被我列为头条编程规范的原则:绝对,绝对,绝对不要使用没有100%提供源码的第三方技术。这是一条红线,不管这个技术有多强大,都绝对木有例外。程序猿们或多或少都有感触,在编程的世界里,CPU时序的不确定,存储IO的阻塞,其他进程对CPU/内存资源占用造成的扰动,后台进程如杀毒软件偶尔的锁定文件访问,公网路由的拥塞,都为运行着的程序施加了太多不可预知,不可控制的因素。而在这些不可控制的因素里面,允许在自己进程的地址空间内运行一些无法得知其本来面目的代码,是其中危险也是容易失控的那一类。反面例子太多,俺就不举了,也免得触物伤怀,影响心境。 ...... 关于“三个绝对”的问题,俺专门补充说明一下,
其实到了关键时刻,有代码在手上,就是一颗定心丸,正所谓“源码之前,了无疑惑”。当遇上奇怪的症状时,什么文档都比不上正在运行着的唾手可得的鲜活的代码。 接下来是针对引擎的两种主要的开发模式的选择,明面上的优势和劣势已经标注在图中了。简单地说,如果“求快” (往往是需求方更强势) 占了上风,往往是“钻进去改”的框架式更合适;而如果“求稳” (往往是实现方更强势) 的占了上风,则往往会以相对严谨的工程化思维来设计架构和实现。有的团队自打一开始就压根就没考虑过这问题,管它三七二十一先改了再说。一上来堆系统堆得很爽,进度喜人,到了后期处处是改到一半改不动的烂摊子,这种时候再加班加点加人手,硬啃硬怼硬编码,拼命拿战术上的勤奋去掩盖战略上的懒惰。 接下来就到了具体内容的考察了。这一页上列出的三个因素是需要考虑的一级要素。为什么说 (从引擎角度看) “这三个因素直接决定了项目按期交付的可能性”呢?这是因为,对于给定的游戏类型和设计,这些因素直接影响到一个项目实际工程量的大小。
由于在头文件的包含传染性和预编译的物理依赖关系上有欠考虑,不当依赖导致修改时的重编传染性极强,进而导致我们深入研究出一系列“避免动到头文件就能搞定需要的功能”的偏门神功,实在是说多了都是泪。好吧,反正这里已经黑了一把 UE3,干脆再来一个,负负得正吧。UnrealScript 是 Epic 专为虚幻引擎实现的脚本语言,无奈这货跟 C++ 的关系实在太紧密了 (如只要在涉及 native 的情况下改动变量就得重编 C++),紧密到运行时的动态能力已经损失殆尽,几乎已经没法拿来当脚本用了 (比如像 Lua 或 Python 那样轻松地在运行时 make change / run / reload )。好在 UE4 里已经把它去掉了,这里就不多说了。 这三个方面都会直接影响到一个项目的工程时间开销,是一级的重点考察因素。 接下来是次一级的考察因素。这一级的考察因素侧重于引擎的工程质量,毕竟说到底,游戏引擎是一个软件项目——功能再花哨,如果根基不牢,那么做出来的游戏多半也是摇摇欲坠。团队规模越大,受工程质量高低的影响也会越大,严重时会直接影响到整个团队的节奏和士气,进而成为影响交付的难以克服的风险。 我们先来看耐受力 (exception tolerance) 。什么是耐受力?简单说就是对非正常流程的处理能力。能够使用系统性的策略,而不是事到临头草草地 assert 来处理非法输入;能够结构性地把自己可以识别和处置的错误从无数的未定义行为中区分出来。说白了,耐受力意味着“工程上的安全感”。 举个例子,同样是使用未初始化变量,在C/C++里你可能啥事儿没有,也可能直接宕机,也可能在极为偶然的情形下宕机,也可能看起来没啥事儿而过很长时间以后宕机。而在 Lua 里却不会有问题,并不是后者比前者高明,而仅仅是因为它对这一类问题有良好的定义。 再比如说,一个编辑器,指定的操作稍微没有按照事先定义的流程来,立刻 assert ,非常敏感,一有风吹草动就 halt。很多程序员喜欢辩解说“尽早崩溃”是佳实践——在遇到问题的时刻报错,报得越早越好,毕竟越接近问题发生的现场越方便他们调试。这个说法本身当然是没问题的——如果你用个默认值糊弄一下,或者是写某种容错逻辑来忍耐了破坏假定的行为,那么错误很有可能会蔓延到之后更加远离出问题的地方才爆发,那时丢失了源头和上下文,查错的成本会变得非常高。 以下详细说明部分节选自俺的一篇未发布的文章,详细说明了一下这个问题。 如果只考虑程序员,不考虑团队中同样依赖每日版本来工作的策划,美术和测试的话,这个思路是没有问题的。然而跟程序员不同,当发生崩溃时,团队内的其他成员能做的非常有限——以快速度通知程序员,版本挂了 (The build is broken!!!)。如果坏的地方正好是他们工作的部分,那么他们只好停下来,等待修好才能继续工作。否则要么一直备有一个可靠的老版本 ,要么手动回滚。 现在请摘下程序员的帽子,假设自己是一个负责“测试多人副本,任务和活动”等业务逻辑相关的测试人员,每测一次都要花不少时间进入测试情境,偶尔甚至需要多人一起协同测试。那么一个跟你的工作毫不相关的底层崩溃,所带来的影响就会被迅速放大,很可能得完版本后的几个小时就在反复尝试和等待之中被消耗掉。 这是一种惊人的浪费。 给程序员巨大便利的“尽早崩溃”,对非程序员来说,意味着日常开发中的每一天,都要冒着被“不可控的因素”延误工作的风险。有人说,正确的姿势难道不是让程序员有更好的自律,在提交前做尽可能充分的测试,确保不要搞破坏吗?是的。可是即使是经验丰富的程序员也不能拍胸脯保证自己 bug-free, 更不能保证由若干人提交的若干“不相干”的改动集成到一起就能无缝地良好工作。让非程序员去承担这种因为版本不成熟导致的效率折扣,是既不公平也不高效的。 说到“巨大便利”,不得补充一个前缀——“本机上的”,也就是说,只有崩溃恰好发生在制造这个问题的程序员的机器上 (或可以方便地即时远程调试) 时,这种巨大的便利性才得到体现。考虑到发生在非程序员环境下的崩溃,不少情况下是由于环境配置错误等杂音所致,“有经验的”程序员往往不会浪费他们“宝贵的开发时间”,时间赶往现场开始分析和调试(打断自己的工作跑去协助调试,满头大汗弄了半天,发现是环境配置的问题/版本问题/别人代码导致的问题,足以唤醒一个温顺的程序猿内心的洪荒之力了)。更多的,他们会在 IM 上回个消息:“嗯,这个功能我提交前测试是正常的——你的环境干净吗?需要的数据都干净地重新生成了吗?第三方库的二进制文件更新了吗?你们几个人测试的版本一致吗?要不你 Cleanup / 重启 / 重新保存 / 重新建个账号试试?”,试图通过尽可能小的时间开销来帮助诊断和解决问题。长远来看,这些试图节省调试时间的沟通,会让“尽早崩溃”所带来的巨大便利慢慢地挥发殆尽。 一个不那么容易觉察却更为严重的系统性问题是,总是采用“尽早崩溃”的实践的团队所产出的代码库,随着系统内不同模块之间的交互(以及随之而来的各种假定)越来越多,往往倾向于通过更多的断言来让系统变得越来越敏感和脆弱。因为,认真细致地考虑模块间的依赖时序,并系统性地从结构上解决过深的模块间耦合,总是比一个简单的断言要复杂得多。 “尽早崩溃”的主张是如此的简洁有力,以至于我们在那些应当通过改良结构,去除耦合来解决问题的时刻,往往简单地选择了使用断言来做一个时序上的约定。这种显式的指定会把系统的坏气味转化成太多的不必要依赖。的确,问题从表面上看起来变得更简单了——谁破坏了断言,导致了崩溃,谁就修呗——实质上,修来修去,把一个本质上可以剥离的简单交互,变成了严重依赖各种时序和条件的“靠巧合工作”的杂耍系统。 你看,“尽早崩溃”的简单性和便利性,在一些情况下反而成了一个让代码质量退化,鼓励系统熵不断增加的问题机制。那么问题来了,在满足了“必要的时候程序应当尽早崩溃”的基础上,还有什么可以选择的实践吗? (以下略)... 到这里我应该已经基本说清楚什么是耐受力了。在图中我提到的三点分别是特殊需求,坏数据,破坏性的改动。这些都很直白,通过验证一个引擎在这些方面的表现,我们很容易对它的耐受力作出判断。 可见性也是重要的考虑因素。如果引擎能充分揭示自己的业务流程 (如何运作),生成的各类数据 (如何存储),关键模块的性能开销 (如何优化),那对各类基于引擎的二次开发才能更有信心,才能够大限度地避免依赖了错误的假定。当出现问题时,也更容易查找和比对。而如何在维护大的可见性的同时保持良好的封装和较低的耦合,同样是一个很大的话题,这里就不再细说了。
游戏引擎的运用和改造 说完了引擎的评估,接下来的运用和改造是很大的题目,对不同的项目类型也不尽相同。到这里不知不觉已有上万字了,为免冗长,我们提炼出一些相对通用的考虑和实践,就不考虑在单方面深入讨论了。 在一个项目内涉及到引擎相关的部分,首当其冲的就是工作流的管理。项目是由人构成的,一个进行中的项目包含了许多显式或隐式的流程、约定和步骤,这些交互交互随着开发的进展不断动态变化。正如左下图那样,每个团队成员作为其中的一环或多环,有机地交互并推动项目的进展。要想让这个系统持续地有效运转,很重要的一点就是通过不断的观察、定位、梳理,来改进系统中响应速度跟不上整体节奏的环节。 在做阶段性回顾时,我们容易把目光聚焦在“什么做了,什么还没做”上,却容易忽略对影响响应速度和导致效率损失的因素的及时处置。如果能够持续不断地观察和优化这些敏感点,我们就能发现,抛开每个成员技术方向和能力的差别不谈,绝大多数响应问题是由于 (过多的) 依赖导致的。这些依赖既有内部的,也有外部的;既有业务逻辑需求驱动的逻辑依赖,也有物理性的资源和数据依赖——当然多的还是由于“针对工作流的分析和梳理严重不足” (俗话说的做到哪儿算哪儿) 导致的项目成员之间的无谓依赖。 有种常见的说法是 Daily Build 就是项目的心跳,保证每日构建的安全、自动和鲁棒是重要的。然而我认为这只是工程意义上的心跳,一个游戏项目的真正心跳在于“持续的可感知的进展” (Continuous sensable progress)。一个游戏项目内,任何一个可感知的点,不管是策划针对某个角色某个技能的构思或数值调整,还是美术对某个场景内某个特定氛围的塑造和创作,还是服务器程序对于一个特定功能 (如快速组队匹配) 的效率上的优化,都会通过或长或短的工作流程,后在某个 Build 内以游戏内的一个可感知的点体现出来。我深切地感到,这样的基于实际体验点的持续而有节奏的交付,才是一个健康的游戏项目的真实心跳。而这个真实心跳是否能良好运转,跟工作流的响应效率和依赖处置是直接挂钩的。 关于 hackers & scientists 的区分对待,也是值得讨论的一点。“黑客”式的工程师会准确而锋利地切中要害,他们敏感,敏锐,敏捷,有惊人的直觉和洞察力,对大部分问题都能在很短时间内直截了当地给出“行还是不行”的答案,相应地,他们多数时候“事了拂衣去”,不那么在意严谨和完备,不愿意陷入琐碎的工程细节,也对编写日志,测试用例等等一切“官僚主义的形式材料”满不在乎。而与此相对的是,“科学家”式的工程师们,普遍周全,周密,细心和细致,能不厌其烦地追究每一个细节并给出妥善的应对方案,他们无比在意工程的完整性和完备性,产出的代码精确、详实而可靠,充分而周祥地考虑了各种可能出现的隐患和边界条件。 很少有程序员能同时具备黑客和科学家的素质,所以这就要求我们能够感知并理解每个个体的行为方式,不断做针对性的调整和细化,从而让他们的积极特性在项目中能得到大程度的放大。 这里是关于同步节奏的管理,图上已有表述,不再多说。 针对引擎局限性举的 Gamebryo 例子。 这是魔兽世界里的兔子,详细的材料可见这里:有哪些看起来很高端的技术其实原理很暴力很初级?。 引擎不支持某个特性,并非总是坏事,也许正是你的游戏体现出差异性的好时机。有段时间,使用 UE3 开发的游戏扎堆出现,其中那些质量低下的作品,几乎总是让你一眼就能看出是在 UE3 上随便堆砌了些美术素材就放出来的昧心之作。而那些真正下功夫的 3A 之作,却总是能跨越技术的藩篱,(至少是在某一方面) 塑造出超越引擎能力的独特的体验。举个例子,如果 Android 本身在国内体验足够好的话,当时小米 MIUI 的独特本地化体验在 Android 阵营里也就没法那么出挑了。 什么是伪命题呢?就是那些本质上不存在,却因为某种局限性,稳定性或性能问题而浮现出来的需求。作为程序员,我们经常在提炼 xx 需求的时候发现,只要能把 yy 和 zz 弄好, xx 的问题自然而然就消解了。但机会窗口并非总是存在,也许一下没想通,没理会 yy 和 zz 的潜在问题,手一抖把 xx 做了,之后叠床架屋地做了 n 层,然后再想回来改就已经改不动了。举例的话,不少游戏的热更都是如此,就不具体说了。 关于技术负债,只要能意识到这很大程度上并非负面的因素,不要有太重的心理负担就好。在处置这些欠下的负债时,要有勇气不断地断舍离,随时扔下负重,轻装上阵。不要舍不得删代码。 关于删代码有一篇非常有意思的文章,非常推荐阅读: (需翻墙) 如果上面的链接无法访问的话可以看下面这个 Internet Archive Wayback Machine 版本的: (需翻墙) 这篇文章在 上的评论也很有趣,一并推荐。 我曾不止一次地听到有项目组在一个进行中的项目里热切地讨论换引擎,当问到我时我常常会尴尬地笑笑。比较而言,这种动议的出发点往往是产品角度,一般出自项目经理或产品负责人,很少由工程师提出,故而讨论的结果不会影响到实际的执行,所以除了笑笑也贡献不了啥有意义的想法。实际情况是,更换引擎是一个难度指数显著较高的操作,一般的团队不一定能克服得了这个困难,而这种风险往往会被 (有意或无意地) 低估。哦,对了,我还发现,那些曾经投入地讨论换引擎的,往往是折腾完了之后早开始怀念在老的引擎上的好日子的同学。当然了,他们会拿出一百个理由告诉你这么干是值得的,嗯,好吧,毕竟生命在于折腾嘛。 (第二部分完) 第三部分:游戏引擎的下一个十年 这一部分主要是我个人对游戏引擎方面的一些非常零碎的和个人化的整理和推断,以幻灯片的内容为主,文字材料从略,见谅。 |