将游戏机制和帧数绑定,给玩家们制造了多少的乐子与麻烦?

在Steam上收获占比四分之三的差评过后,Roguelike名作《雨中冒险2》的新DLC《风暴探寻者》毫无疑问算是做砸了。

好评率最低跌到了24%,国区更是只有8%

假如仅是DLC更新的关卡、幸存者和敌人存在问题,那不买DLC便罢。

然而《雨中冒险2》的本体随DLC的上线一并迎来强制更新,顺带接入数不清的Bug和错误,包括但不限于:随机猝死,掉帧卡顿,纹理缺失,物品不再高亮,关卡变得更难攻略,最终Boss“米斯历克斯”莫名无敌……

结果《雨中冒险2》本体的近期好评率也被拖累到了“褒贬不一”的水平。

原作者Hopoo和他的团队在两年前将《雨中冒险》系列的版权卖给了发行商Gearbox,所以《风暴探寻者》是完全由Gearbox操刀的第一款《雨中冒险》DLC。现在Gearbox不仅需要承受来自Hoppo跟玩家们的怒火,还得担起全责,修复他们自己给游戏带来的新问题。

8月30日,即《风暴探寻者》上线两天后,Gearbox发布了一篇开发日志。日志列出的海量问题中,第一条且最致命的一条如下:

“游戏中的许多元素都受到一个Bug的影响,这个Bug会导致它们的行为根据玩家的帧数而改变。这些要素包括物理系统、攻击速度和移动。”

“下个补丁会修”

官方用了一周时间才修好这个Bug。在这一周里,他们推荐的权宜之计,是在设置里锁定游戏运行帧数至60帧。仅在60帧下,《雨中冒险2》的宇宙才会按照原本的节奏平稳运行。

而在不同配置的电脑所带来的不同帧数下,游戏内发生的一切也将产生加速或减速。倘若取消帧数限制、放任帧数随意增长,这个宇宙便将在加速的熵增中制造无尽的混乱和灾难,气得难以招架反复重开的玩家幸存者们捶胸顿足——

将帧数和机制强行绑定在一起,会给游戏带来深重的灾难。想说明白这条真理并不是件容易事,但这个Bug版本的《雨中冒险2》提供了绝佳的范例。

1

大体来说,因为玩家角色性能受到帧率影响,许多角色在高帧率下都得到了加强,乍一看搞得《雨中冒险2》像是一款变相的“P2W”(氪金致胜)游戏,电脑配置越好,角色跟着越强。

高帧率下,角色的武器射速和技能冷却时间也都得到了普遍加强。比如,武器射得更快,位移闪现技能飞得会更远。“工程师”的跟踪鱼叉提升了锁定速度,“呛鼻毒师”的吸血技能也加快了吸血速度。

30帧和450帧下“指挥官”R技能的射速对比 Youtube@Neon Shockz
高帧率下转起来了的“装卸工” Youtube@Neon Shockz

像“船长”的霰弹枪,需要通过蓄力缩小弹丸的扩散范围。但在高帧率下,弹丸的散布面积比以前更加集中,推测是缩小散布的公式也受到时间影响,随着帧速率提升发生了数据变动。

图源Reddit:u/bataloof

而在锁定30帧的情况下,这些技能都会变弱,弱到有些滑稽的地步。

“雇佣兵”的起飞变“陆游” Youtube@Neon Shockz

按理说,《雨中冒险2》不算是太新的游戏;虽然是具备超大数据计算量的肉鸽,但不去刻意刷道具死堆角色属性的话,也尚不至变成“显卡杀手”。高帧率带来的高角色性能,理应算作一次具有普适性的增强。

可是受到帧率影响的,不仅只有玩家这一边。

有些敌人在高帧率下倒是变笨了,只会反复起跳停在原地。也许有一行代码决定敌人每隔几秒起跳一次,而过高的帧数大幅缩短了敌人的起跳间隔。

Youtube@Neon Shockz

但敌人的数量(生成速率)和质量都在产生质的飞跃,让玩家们猝不及防——猝是猝死的猝。

许多敌人都有锁定玩家后立即发射的激光,在高帧率下,激光的锁定速度快得出奇,几乎无法躲避。前期Boss“石巨人”同样拥有激光攻击,还是每帧都要计算的持续性伤害,高帧率时出伤速度也跟着提升,瞬间蒸发玩家角色的血条。

*惨死* Youtube@Neon Shockz

另一个飞行Boss“漫游者”濒死时会释放超新星爆炸,对附近目标造成致命伤害,前期威胁巨大,但它爆炸前会留出一段时间供玩家规避。高帧率会缩短这段时间,让这个自爆攻击变得躲无可躲。

蓄力效果都没显示完,它就爆炸了 Youtube@Neon Shockz

类比这两个Boss,敌人也都随着帧率上升得到属性增强,增强的力度还远胜过玩家方,秒杀玩家的情况十分多见。

不仅如此,包括一些地图上的中立设施,也会因为帧率问题无法正常工作。

“跳板在高帧率下无法工作”

倘若出于各种因素(比如叠了几十层数值道具、后台跑了太多程序)导致帧率降低,虽然敌人性能也会下降,但玩家的性能也会跌到离谱的地步,乃至跑都跑不动。总之不锁60帧的话,Bug版本的《雨中冒险2》玩着太难了。

既然说是Bug,就意味着游戏代码中的某些地方出了问题。比起Gearbox要花一周时间才能修好问题的程序员,有一批人对代码层面的东西更敏感。他们是《雨中冒险2》的Mod作者,不下半天就找出了Bug来源。

问题出在《雨中冒险2》使用的Unity引擎。简而言之,Unity引擎下有两个函数:Update和FixedUpdate。在Update函数中的所有操作都是按帧调用,而FixedUpdate则以固定的、预设的现实时间(或称固定时间步长)进行调用,默认是0.02秒调用1次,即1秒50次,频率可以修改。

60帧下,Update每帧(约0.01666秒)调用一次,FixedUpdate则不受帧数影响

Gearbox有意在这次大更新将《雨中冒险2》升级至最新版Unity,却好心办了坏事。改代码过程中,程序员自作主张插入了一大堆额外函数,改着改着,就把本该用FixedUpdate调用的、按现实时间计算的数据,混淆成用Update、结合帧数计算了。

“为什么”

发现并在Reddit上曝光这一点后,从Mod作者到普通玩家们无不又惊又气。

因为搞清楚Update和FixedUpdate的区别,是Unity开发者的必学基础课程;将游戏帧数和机制脱离绑定,或者说将游戏画面显示的“渲染帧”和程序计算的“逻辑帧”脱离绑定,也应该是现如今每个游戏开发者的必修课程。

某模组作者的直球辱骂:“他们是低能儿吗?”

2

Unity是一款足够成熟的引擎,将游戏逻辑和渲染、机制和帧数脱钩的相关功能做得十分完善,网上的教程也一搜一大把。

然而几十年前的游戏开发者可没有现成的引擎和如此理想的开发条件。他们也根本没可能意识到帧数绑定游戏机制可能酿成的灾难。因为帧是程序运行和画面渲染的基本单位,把游戏里发生的一切跟帧数联动起来也天经地义、顺理成章。

网上能够搜到的、建议开发者将帧数和机制脱钩的英文教程,最早可以追溯至2004年。那个世代属于PS2、Xbox初代和NGC(任天堂Gamecube),PC游戏也方兴未艾。

“修复你的时间步长!”

而在这个世代及以前的绝大部分游戏,永远将帧速率和游戏机制绑在一起。就和臭名昭著的“千年虫”(Y2K)问题一样,游戏开发者更多考虑当下而非将来的需求,而玩家们尚没有能够显著提升游戏帧率的设备。

像是FC这类复古主机,或者同时代的街机,机能相对受限,一旦游戏画面出现了太多运动的物体,游戏画面就会卡顿,帧数暴跌;不仅如此,游戏里的物理时间、物体运动速度也会一并放慢,进入“子弹时间”。

这即是帧数绑死游戏机制导致的,要慢一起慢。不过玩家们并没有因此吃太多亏,“子弹时间”偶尔也能帮助玩家及时反应做出救命操作。

FC掉帧王《魂斗罗力量》,手柄开了连发也会因为掉帧丢一部分伤害

21世纪初,玩家所能拥有的硬件设备,特别是PC设备,迎来了突飞猛进的发展,在60帧以上运行游戏不再是梦。可运行在这些设备上的“老”游戏,开始出现数不清的问题。

从30帧到100帧,问题其实还不是很大,一些拥有以当时标准来看算高配电脑的玩家,早早利用起了高帧数或固定帧数带来的优势。

像是在世纪初头几年的FPS游戏,角色的移动速度都会受到帧数影响。在CS1.6的经典地图之一“de_nuke”,也就是后来的“核子危机”,T阵营出生点右侧有个十分陡峭的斜坡,只有60帧以上的电脑才能向上爬;假如电脑仅带得动30帧,这辈子都别想上去。

都市传说是真的

《雷神之锤》和早期几部《使命召唤》属于另一种情况:将帧数锁定在一个特定的数值时,角色会比平常跳跃得更高、更远。

这是由游戏结合重力计算玩家速度和向量的公式所决定的。以《雷神之锤3》举例,在帧数设定为125帧时,玩家能够跳跃至60帧环境下不可能到达的平台。因为这游戏的速度计算公式在每帧最后都要进行一步四舍五入,而在125帧下得到的数据会取更多的“五入”,造成略高于预期的误差。

无限制帧数,不行;125帧,彳亍
按公式算333帧下玩家角色跳得最高,但没必要,图源Youtube@Matt’s Ramblings

从100帧到300帧,游戏就会像Bug版《雨中冒险2》那样,运行速度整体变快,偶尔陷入无法攻略的混乱中。好比上百帧运行时的GTA老三部曲,主角的移动速度、造成和受到的伤害都会提升,任务的倒计时也会加速流逝,再加上一系列Bug,给玩家徒增麻烦。

死于大跳的冷面杀手 Youtube@nikitozz
无法正常索降的精锐特警 B站@R星老清子
水压不足的消防车 B站@R星老清子

从300帧到3000帧,由于逐帧播放的菜单动画也加了速,屏幕前的玩家甚至没办法选中“开始新游戏”的选项,更别说玩了。

平均4000帧下一路疾奔、逃脱不出演示动画的《东方红魔乡》

包括一些在主机端以30帧运行的老游戏,如果强制搬到PC端的模拟器,再以60帧运行,也会冒出各种各样的Bug。

想让游戏变得正常,要么想办法重写代码,先让一部分重要的机制和帧数脱钩,起码让游戏能玩得下去,但这一招优化成本太高;要么用各种游戏内外的手段强制锁定游戏运行帧数,比如官方提供的硬锁和垂直同步功能,以及民间自行开发的模组,做到低成本甚至零成本。

由于过时引擎的底层代码留下的永恒限制,想让一些老游戏提到60帧运行都成了奢望。

例如《命令与征服》和《红色警戒》系列所使用的引擎,就会逐帧推演时间流逝,顶多跑30帧,多不了一点。倘若强行使用某些工具突破帧数限制,单位的动画跟AI的操作都会变得鬼畜起来,只怕玩家还没操作造出第一个动员兵,对面就派出坦克把玩家基地送上天了。

最快速度=锁30帧

3

就算在十几年后的现在,游戏行业也无法摆脱帧数的无形大手。只要不影响极端配置下的游戏体验,闹出《雨中冒险2》那样的笑话;或者能够通过锁帧等手段掩盖可能出现的问题,游戏开发者大多会对这个问题睁一只眼闭一只眼。

不是高手或速通大佬的多数玩家,想必都不太会在意,《怪物猎人:世界》里太刀的登龙或弩炮的贯通弹在某些帧数下少跳一次伤害数字,或者《生化危机2重制版》里昂的小刀在120帧下伤害比60帧要高。

速通网站都要留个单独的120帧选项以示严谨

卡普空近年来的3A大作所使用的RE引擎,也是一款常见的会受帧数影响游戏机制的引擎。这款引擎中的大多数代码其实都是基于现实时间而非帧工作的,但卡普空还是把最重要的动作和伤害判定部分交给帧数来操控。

《怪物猎人:世界》贯通弹跳伤害次数也随帧数变化 Discord:Moonbunnie0001

事实上,不少的游戏引擎都采取了类似的双轨制,将一部分跟游戏逻辑相关的机制交给现实时间,将另一部分需要严格且忠实地渲染在画面中的机制交给帧数。

无论是在欧美还是在亚洲,游戏开发者都普遍不愿放弃帧数对游戏机制的影响。这并不一定是因为他们犯懒。

首先“祖宗之法不可变”,许多十几年前的引擎如今还在用,代码修改难度和成本实在太高。例如《命令与征服》进入3D时代所使用的SAGE引擎、卡普空的RE引擎,乃至B社的Creation引擎、R星的RAGE引擎、FromSoftware自研的祖传引擎,都会因帧数变动产生物理机制的变化。

《黑暗之魂2》旧版本的武器耐久度会在60帧加速消耗,但这一点是可以被修复的

何况这世上从来不是只有PC玩家。主机帧数稳定,主机玩家没理由考虑在60帧以上运行游戏的问题,一些将主机玩家视作主要用户的游戏,根本不需要考虑脱钩的这一步。

而且这一步说着容易,实则意味着要让开发者多写成百上千行的代码。这些代码会对硬件造成额外的负担,占用大量性能,提升单平台或多平台的优化难度,乃至拖累游戏渲染,造成画面撕裂。

那些注重动作渲染的游戏,包括《街头霸王6》这样格斗游戏在内,更不可能冒着破坏动作同步和流畅度的风险,将逐帧进行的动作强行绑定现实时间。比起浪费时间优化代码,开发者宁愿采取更简单粗暴的锁帧技术,避免高帧率设备破坏游戏平衡性。

所以,即便Unity明确区分了帧数调用和现实时间调用的两种函数,用Unity打造的《原神》和《永劫无间》最终还是变成了“高帧数等于高攻速”的典型代表。

《原神》不同帧数下的攻速对比 B站@紫菜蛇皮汤

另一个使用Unity的知名作品《绝地求生》,在多年前其实也有帧数影响武器射速的说法,好在这一点最后靠着持续的优化调整得到了修复。

什么时候这一点不需要修复呢?当然是故意把帧数对游戏机制的影响,做成游戏设计的一部分。目前看来,估计也只有那些玩梗的Meta游戏,想到反其道而行之,用这套系统逼着玩家拉低帧数。

2020年的比萨互联网节日游戏大赛上,一款叫做《让我卡顿》(Make Me LAG)的小游戏取得了一等奖。在这款横板游戏中,主角仅在60帧以下时才会开始行动,要求玩家采取手段调控游戏运行的帧数,进行移动、跳跃、攻击等操作。

45-59帧移动,25-44帧跳跃,1-24帧攻击

国产Roguelike游戏《清零计划2》于今年推出的更新中,也添加了“帧数越低伤害加成越高”的词条设计。

帧数和游戏机制之间剪不断理还乱的关系,不一定非得是令游戏开发者头疼几十年的梦魇,也能变成鼓励玩家积极利用的巧妙设计,是开发者的挑战,但某些情况下也是机遇。

要我说,《雨中冒险2》也是肉鸽,比起掩盖过去的错误,不妨把Bug代码留个备份。《雨中冒险2》拥有一个神器机制,神器相当于《雨中冒险2》的内置修改器,能够彻底改变一局游戏的体验。将游戏机制绑定游戏帧率的Bug,以及这个Bug带来的混沌宇宙,就挺适合做成一个官方神器,既有乐子,又有诚意。

不过想不想做和能不能做是两码事。古往今来那么多的游戏开发者,都无法解决帧率和游戏机制的问题;对于修这个Bug都能修上一周的Gearbox程序员,还是不要给他们添加太多工作负担为妙。

他们能让《雨中冒险2》走出代码错误和Bug满天飞的窘境,玩家们就知足了。

部分参考资料:

https://gafferongames.com/post/fix_your_timestep/

https://gameprogrammingpatterns.com/game-loop.html

https://docs.unity3d.com/Manual/ExecutionOrder.html

https://docs.unity3d.com/Manual/TimeFrameManagement.html

https://www.reddit.com/r/Cynicalbrit/comments/3dqwga/the_fps_lock_what_it_is_why_should_you_avoid_it/

https://www.youtube.com/watch?v=he02vJvKaRs

https://docs.google.com/spreadsheets/d/e/2PACX-1vSM-BTBvmvsovRJ2SaYdLLutC_CImTKpFh4GXcb3bNimjvbb54hGNFb2Nwne2zBJIkPR00zKGxOO4C0/pubhtml