那些日子(十九)

不计算周边工具,我一共为《梦幻西游》贡献了引擎部分(包括图象、声音、网络和 UI 以及对象管理)三万多行 C++ 代码。这是今天用工具统计出来的数字。这些全部是在大话西游二完成后,从零开始写的,几经增删留到今天的部分。

对于梦幻的 client ,郭老的代码贡献量更大。除了保留修整少量我从大话二遗留下来的底层部分外,他写了个 python 的接口部分,和所有的游戏逻辑部分的 C++ 支持。再次设计,我们做的比以前好。代码很精简,而完成的功能要多的多。依稀记得总共 C++ 代码不会超过五万行吧(手头没有数据可供统计)。而对应的大话二的 client 代码量则是图象及声音的引擎部分二万七千行,逻辑和网络及 UI 部分三万一千行。

我想,合理的构架下,代码的体积应该是更为精巧,而不要过于膨胀。代码写的短小,是自然而然的结果,其成因是避免了重复。代码行数多少,则不是刻意去追求的。对于网游 client (也包括 server ),都不应是超过 10 万行 C++ 的规模。从程序角度上说,网络游戏是个小项目,不是吗?如果有人硬把一个小项目做成了大项目,不出问题才怪。

郭老是个很勤奋的人,非常主动的完善程序。当时他改进了地图文件的格式,重写了地图数据生成工具。对于已完成可以正常工作的东西不满足,自己去改进。这样的程序员对项目非常难得。

这种在这种追求完美的心态下的产物,几乎不可能从工程管理角度上去保证有人做。这也是我后来作为项目管理人员也一定要自己参于写代码的原因之一。对于一个项目,如果我们只从完成功能角度去验收,充其量只能达到合格,远谈不上优秀。何况是一些很难度量的质量、细节方面,做到怎样的程度可达到多少收益,这中间的关系没有人说的清楚。

想把游戏项目塞进一个流水线中,拧上闹钟,到时间了来收割。绝对是一个笑话,只存在于投资人的梦想中。

梦幻里的很多画面细节,并非专职策划人员写进文档、提出需求,然后交给程序员实现出来的。那些日子,郭老玩了许多网游,他自己就是个《魔力宝贝》的玩家。看到别人游戏中有趣的亮点,就想办法实现。同时也想一些有趣的东西加进去。我也乐于配合。

到梦幻发行时,许多老朋友表示游戏挺好玩,有些单机游戏的感觉。这中间,大家对细节的把握甚关重要。既然是互动游戏,我们就应该和提交电子表单区分开,游戏玩的是过程,而不是结果。


我想梦幻这个项目,大家都很努力,并不因项目没有指定确切的结束日期而松懈。记得那个夏天的一个周末,有 QC 部门的同事报告了一个 bug ,会引起 client 崩溃。但是 bug 如何发生的,毫无规律可寻。只是玩了几个甚至十几个小时后,程序异常退出而已,没有任何方法可以重现错误。发生的概率也非常之低,几天才碰到一次。出错之后,查看调用栈,发生问题处的代码逻辑一切正常,引起问题的是一个对象里的数值不太正确。而这类对象在内存中有上千个同类,每次出错点都不太相同。

有过程序调试经验的同学当知道,这种 bug 是极其难追查的。你无法通过出错现场判断 bug 成因,无法设置断点来侦察早期的内存异常状态。通常只能靠阅读代码来分析那些部分写的有问题。当出错现场周围的代码正确时,程序员面临的可能是通读所有相关代码。

如果没有一切 bug 都是可以追查出成因的信念,我想对于这种出现概率相当低又束手无策的问题,对于很多急迫的项目都会暂时搁置。而郭老和我都认为,不查到原因,就一定不能把程序做下去。bug 就像心里的疙瘩,解不开,干什么事都不舒服。

记得那几天,我们从周四找到周六,一直没有进展。周日上午,我在健身房推杠铃,突然手机响了,郭老兴奋的说采集到一个录象,在启动 client 开始半小时后就程序崩溃了。前面谈过录象文件对 bug 追踪的帮助。之前面对这个问题时,录象都是长达几个小时的。我们为重现一次现场就等上几十分钟,那样效率实在太低。当采集到这个只有半小时的录象时,意义非常重大。立刻打车回来公司,我们两人就在空空的办公室中,调试这个问题。

事后的发现让我们解开了心结。真正导致问题发生的时刻比引起内存错乱的程序崩溃点提前了几分钟。是由于 python 和 C++ 的接口处,一次增减引用的次序写反了。这使得在某一瞬间,python 引用了一个刚好被释放的 C++ 对象。通常,引用随即被解开。但在一种巧合下,中间一块内存的指针有可能被传递到别处重新引用起来。这种错误的引用关系保持了很久,在后继的一系列临时对象销毁过程中暴露出来。

这是我职业生涯中,最难调试的一个 bug ,印象极为深刻。以后再少有对已知 bug 三天内还解决不了的了。


除了忙梦幻的事情,还有大话方面的一些工作。

03 年 2 月的时候,我跟 dingdang 聊天,说能不能在大话二中试着加一些卡片类小游戏。可能我受 console game 的影响比较深,许多 console game 都在主体游戏之外,还做一些分支游戏(比如最终幻想 X 中的水球)。这些分支游戏并非零碎的硬凑到里面的小游戏,而是也有它们的线路。

我的建议是在大话中增加一种卡片收集对战类的游戏。但是大话二的策划组不太愿意整体的来考虑这件事情。他们担心工作量太大,并且不能预知对游戏整体系统的影响,这种心情是可以体谅的。后来我拉着叶航一起来做大话牌,考虑到重新设计规则需要投入太多精力,便直接搬来了最终幻想八中的卡片游戏规则。这个对于程序很好实现,而我的想法是想在大话中试验多条游戏线路,具体这第二线路的玩法就不太在意了。

事实证明,独立于游戏主体规则之外的第二游戏玩法是不能成功的,玩家不买帐。这也算是一次宝贵的经验吧。事后想,若是把奖励和主体规则挂钩会好一些。这件事让我明白了,网游和从前单机游戏不同,玩家有很深的成果导向,对游戏性本身的注重远不如单机游戏那么多。

倒是为这个大话牌制作的美术资源没有浪费,这也是我们立项开始做前就想好的退路。我们为之设计了变身卡的玩法,可以充分利用外包美术制作的卡片图案。整个大话牌的制作,全程都有叶航的参与,他编写了所有的文档。不知道这件事对日后设计天下二的元魂珠变成有多大影响。

这段故事中间有个小插曲。在做卡片调落和集换规则时,我想尝试做一些新的东西,不以调落概率来控制卡片的稀有度,而做一个中心服务来统一发放卡片。kyo 对这种做法非常的反对。他认为,一切程序都是不可靠的,我们应该绝对避免中心式的数据服务,因为一旦中心式的服务出现问题,所有游戏系统中的玩家都会受到影响。按照这个思路,在服务器中出现唯一(或有限)资源就是应该回避的设计。他希望系统能保证:每个玩家的私有数据,都不跟其他人发生交互。(这种交互应该从广义上理解,比如有样虚拟道具在服务器中需要严格存在 10 件,那么你能不能拥有这件道具,以及获得这件道具的概率,就跟还有哪些人已经拥有了发生了关系,这也是一种交互)。也就是说,服务器程序应该对任何一个玩家的服务都是独立的。

而我站在了他的对立面,按我的想法,网络游戏的发展趋势应该是交互。这可以提供游戏更多的游戏性。稳定性第一、游戏性第二固然正确,但是死守着稳定性,而不尝试在游戏性方面做一些技术突破是不行的。这个问题和 kyo 争论了很久,最终的结果是我自己实现了代码,并在数据可靠性方面做了额外的保护(为此专门写了一个双份交互备份的简易数据库防止系统以外掉电引起的数据残缺,以此用来保证卡片发放的正确)。

让程序员做自己不认可的工作是很难做好的,很多情况下,自己来实现是无奈之举。这也是为什么许多不懂程序的策划难以把游戏做好的原因。因为最终的实现还是在程序员手上,同样的工作,做的好和差,质量相差巨大。有些设计,看起来很美好,但是很可能因为实现质量不够,好事也变成了坏事。作为游戏策划,当然希望做实现的程序员可以懂点游戏,这样写程序的时候更容易达到预期的目标;但程序员太懂游戏了,不免有自己的想法,我们又很难让他们心甘情愿的去按自己的思路做。

另一个是做大话兵器谱,同样是一次失败的尝试。也是想试着在玩家间建立联系,而不是按从前通常的做法:采取简单的积分制。由于策划们的担心,这个系统没有和游戏中的实际利益挂钩,是一个完全独立的系统。刚开始我觉得无所谓,只是先看看这个排名积分系统是否能正常工作。后来才发现,脱离整个游戏的任何独立系统,都是不受玩家欢迎的。

于之可以做比较的是几年之后的 wow 中的荣誉系统,同样是设计一套规则,用来计算玩家间的胜负关系。我们可以设想一下,如果荣誉系统不给玩家荣誉整备这样的奖励,这个系统还有多大的存在意义。

最终,我意识到,其实游戏的策划设计跟程序一样,一个项目永远只能有一个主导人。无论游戏项目如何分工合作,在任一方面,都只需也只能有一个人把握。从此我不再干预公司其它游戏的开发。


03 年,我的生活远比今天丰富多彩。

在我和叶航的倡议下,公司组织员工进修日语。dingdang 专门找了个外语学院的学生来办公室给我们上课,一周两晚。我们的政策是只要考试一次过关,公司免去学费,并额外颁发 1000 元奖金;如果补考不及格,则要交纳双倍学费。平时不准无故旷课。

仿佛又回到了学校。甚至比读大学时更卖力,大学里,我可从来没有哪门课有那样高的出勤率。考试前还连夜复习。到后来真是苦不堪言呐 😀 。第一期结业后,就没再继续。不过收获也挺大,至少打日文游戏时,不至于两眼一抹黑只会认里面几个汉字了。

到了夏天时,我莫名其妙的爱上了自行车运动。怂恿 dingdang 一起去装了两部车。他没我玩的那么起劲。经过半年的锻炼,我的身体状态非常不错,腿部肌肉也挺有力。每个周末都出门骑行,平均时速 30 公里以上,每次都是 100 公里左右。把珠三角逛了个遍。每次都是一个人中午出去,然后到天黑了才回来。

沿途乡村里的人都挺热情,还好奇的问这是为啥。我知道自己看起来就是自找折腾。啥事不干,背着地图,挂上指南针,骑个车几十公里出去,立刻折返。速度还挺快,不作停留,不为欣赏风景。在出城前的那一段,还要忍受一路的汽车尾气。

路上觉得孤独的时候,就放声的唱歌,不着调儿的老歌,感觉很好。每次爬坡的时候都特别累,但我发现,每当爬上坡顶,接下来就是非常惬意的下坡。老天绝对不亏待你,有上就一定有下,有下那么一定接着一段上。

八月,我和 dingdang 约定骑车去一趟珠海,他刚参加工作时干过一次,认识路。我们周六早上九点出发,我来负责背行李,走省道过去。那次是我骑行生涯中最有挑战的一次。太阳很大,晒的人很不舒服。广州到珠海的路很不好走,许多地方满是碎石子。一路好多拱桥,上上下下累的半死。后半程路上刮起了风,一路都是顶风前进的,只能勉强维持 15 公里的时速。路上被铁钉扎破了胎,掉了两次链子,走错一次路,多跑了十几公里。中间有个大下坡时,拿着水壶喝水,一仰头,帽子被吹掉了。下意识的单手去捏刹车,不小心摔了一跤。中午弄的胃口全无,在路边的小餐馆里,就只觉得那两条生黄瓜特别好吃。

到珠海已经是晚上八点。帮我们预先定了酒店房间的赵青一个劲的打电话问怎么还没有到。珠海海滨的情侣路,骑在上面才觉得怎么如此漫长。到达酒店时已是晚上九点,马表上显示超过 150 公里了。

夜里吃完饭去金山的办公室见了雷军,随便聊了几句就回去睡觉了。

第二天我们骑到码头,乘船到了深圳蛇口。从深圳回来的路程也是 150 公里左右,但是路面非常的好。虽然头一天消耗的不少体力,但是轻松了许多。深刻感受到珠江两岸的经济发展差异。

这次魔鬼之旅,我身上晒脱了一层皮,半个月以后长出新的来,痒痒的。可能是因为脱水和劳累的缘故,体重一周内掉下去 15 斤,到 04 年都没完全补回来。后来就没再做长途骑行了,也打消了从广州骑车回武汉的念头。

路上我跟 dingdang 聊了许多,聊我们的项目,我们的公司,我们公司里的人。也谈这个行业,说说听来的一些八卦。诸如盛大是怎么起来的,为什么当年中华网撤资后,陈天桥还有钱去买传奇之类。

这次以后,dingdang 少有时间出去了。接下来几年,我经常组织同事在周末出去活动。但每次叫他,他都很忙。起初我不太理解,认为工作和生活可以分开,周末出去放松一下不会有什么影响;后来跟 dingdang 的妻子在网上聊天,可以感觉到一些他的心理状况,慢慢的理解了。

如今,我自己带了一个团队,更加感同身受。当承担了太多人的责任时,更多的是心累。脑子里自然无法分心做完全属于自己的事情。

发表评论

电子邮件地址不会被公开。 必填项已用*标注