脏代码技巧 精准补丁(外二则)

from 黑客志 http://item.feedsky.com/~feedsky/heikezhi/~8608072/583248750/6713895/1/item.html

精准补丁

有个老笑话是这么说的:

病人:“医生,我一这样就难受。”
医生:“那就别那样。”

有趣,但把它用在自己的项目中也管用吗?这让我想起了自己从PC移植一款3D第三人称射击游戏到初代PS上时所经受的痛苦。

现在,PS1不支持浮点数,所以我们要重新编译PC代码,把所有浮点数都换成顶点数。这确实效果不错,除了碰撞检测。

我们的关卡的模型在PC版里确实运作的很好,但当浮点型转为定点型以后,微小的数值误差使各种拼接缝线、T型拼接处和其他问题层出不穷。这个问题的表现之一是主人公(叫做“Damp”)会轻易掉进那些微小的缝隙中,落入关卡深处。

我们补上了我们发现的缝隙,调整几何模型直到Damp不再从上面掉下去。但在游戏进入测试阶段移交给发行商之后,我们收到了海量的“掉落bug”报告。每天都会有一批新的可以把Damp漏下去的地方被发现。我们修复了一批模型,第二天又会送来十几个问题模型。这持续了几天。后来出版商的测试部门专门雇了一个人每天在游戏里蹦来跳去10小时,就是为了看看还有哪儿能把人漏下去。

问题是模型不好。它不是紧密无缝的集合体。它在PC上可以运行,但在PS1上不行,定点数极大地放大了这一问题。最优的方案是把几何体修补至无缝。

可是,这个工作量太大了,以我们的人力不可能在时限之内找完,所以我们靠测试部门告诉我们出问题的区域。

这个方案的问题是,找出来的地方无穷无尽。我们越来越痛苦。每天都有这个bug的新变体。这似乎没有尽头。

最终问题被解决了。真正的问题不是几何体有缝隙,而是Damp会从那上面掉下去。这么一想,我就可以很快写一个简单的补丁修复这个问题了。修复代码类似这样:

IF ( Damp will fall through a hole() ) THEN
Don't do it

实际代码也没比这复杂到哪去(如下).

damp_old = damp_loc;
move_damp();
if( NoCollision() )
{
    damp_loc = damp_old;
}

这下上千个bug得以修复。现在我们不让Damp掉到关卡下面去了,现在Damp只会在缝隙上抖一下,然后走过去。我们发现什么让我们很受伤,然后我们就不那样做了。出版商解雇了他们的“蹦跳测试员”,游戏发售。

哦,他终于发售了。得益于“if A == bad THEN NOT A”的教训,我用这个方法又修复了几个bug —— 都是与碰撞相关的代码。在开发的最后阶段,bug变得越来越精确,修补的代码也就变得越来越类似“Don’t do thispreciseandexacthing”(别做这件具体明确指定的事)。

if (damp_aliencoll != old_aliencoll
&& strcmpi("X4DOOR",damp_aliencoll->enemy->ename)==0
&& StartArena == 6 && damp_loc.y<13370)
{
    damp_loc.y = damp_old.y; // don't let damp ever
    touch the door.. (move away in the x and y)
    damp_loc.x = damp_old.x;
    damp_aliencoll = NULL; // and say thusly!!!
}

这段代码是干什么的?嗯,首先,当Damp在某关的某处碰到某种类型的门时,会出bug。为此我们不是去追根究源找到那个bug的根本原因,而只是简单地当Damp碰到这个门的时候,就把他移开,永远不让他摸那扇门。问题解决了。

回头再看这段代码让我觉得毛骨悚然。这是在补漏洞而不是修漏洞。不幸的是,真正的修复工作要重做整个游戏的模型和碰撞检测系统,而且是基于PS1的定点数限制。进度表一开始就很紧张了,我们总是身处发货前的修罗场一般,所以快速补丁总是胜过全面而耗时的修复。

但是这也好不到哪去。我们需要上百个补丁,部分补丁自身就有问题,所以它们又需要新的补丁。bug没完没了,而我们就用补丁击退它们。最终我们胜利了,但代价是游戏延期了几个月发售,以及每天14小时,一连几个月的高强度工作量。

这一经历让我很讨厌“补丁”。现在我总是尽量从根部修补bug,即使是一个简单无害、可用补丁掩盖的bug。我希望我的代码保持健康。如果你去见医生时告诉他“我一这样就难受,”那你应该让他告诉你为什么这样会难受,然后治好它。你的痛苦和你代码的bug可能都是某些更严重的问题的症状。教训:你希望医生怎么对待你,你就应该怎么对待你的代码。

– Mick West

你不会喜欢生气的我的

我曾在THQ工作室遗迹娱乐参与制作《The Outfit》,也许有人记得那是xbox 360早期的一款游戏。我们从一个(单线程)PC引擎开始,要在18个月之内用它完成一款多核次世代平台上的游戏。在离发售日还有3个月的时候,我们在360上还是只能跑到5帧每秒。显然这个游戏需要进行一些重大优化。

在我做性能测试的时候,我发现大量代码有效率问题而且写得很“PC”,当然素材方面也有很多问题。有些模型太细致了,有些shader开销太大,有些关卡则是有太多家伙在里面跑来跑去了。

这次的效率问题不是程序员简单地“fix”一下就可以解决的,我们需要的是改变一直以来的一些工作方式,但要说服一个百人团队接受这一观点,是很难的。人们需要了解,游戏的效率问题是我们每个人的问题。最终我决定用幽默一点的方式去解决这个问题。

这个方案画了大概一个小时。一个程序员拿了四张我的正面照片——一张很高兴,一张普通,一张有点生气,还有一张是我在拽自己的头发。我让图片显示在屏幕的一角,然后把它和游戏帧数关联。如果游戏运行在每秒30帧以上,显示我很开心的那张照片,如果帧数低于20,就是我生气的那张。

方案实施以后,帧数问题就从“嘛,程序员会搞定它的。”变成了,“呃,要是我把这个模型加进去,Nick就要发火了!看来我得先优化一下。”人们可以立刻看到他们所做的改变对帧数的影响。最终当游戏发售时,我们的游戏帧数达到了30.

– Nick Waanders

神秘的第10关

当年在[某公司]时,我记的是在[某项目]的收尾阶段,我们有一个对象需要在某一关隐藏起来。我们不想重新导出那一关,而且我们也没有用校验名。所以在引擎代码里,我们加入了类似下面的这段代码。最后游戏就带着这段代码发售了。

if( level == 10 && object == 56)
{
    HideObject();
}

也许一年以后,某个用我们引擎的美术人员会一脸沮丧地跑来问我们为什么把某个对象导入到第10关以后它就不见了。到时候连我也会一头雾水吧?

– 匿名

——–
本文译自GAMASUTRA的 Dirty Coding Tricks  ,原文作者:Brandon Sheffield

本文译者:kira0001

想和我们一道传播黑客精神?快来加入吧!

Advertisements

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s