这是CSDN网站的一篇文章,怕以后这篇文章会被删除,因此保存起来。

StarCraft开发的荆棘之路

摘要:本文作者Patrick Wyatt曾是Blizzard副总裁,参与过Warcraft、Diablo、battle.net、StarCraft、Guild Wars的开发。本文从StarCraft开发的惨痛经历中吸取教训,反思了开发过程中的很多错误行为,实为开发者少走弯路必读之文,相信这些基本思路适用于任何编程。

StarCraft开发启动

开发StarCraft,我们经历了两年半的艰苦岁月包括发布前一年的关键时期,这款游戏曾像一个蚁穴一样,到处都是bug。

它的“前辈”们(Warcraft I and II)远比同类游戏稳定,但StarCraft 却经常崩溃,在测试初期简直是一团糟,而即使在游戏发布后,它同样还需要不停地打补丁。

这究竟是为什么?原因有很多很多。

宇宙中的Orcs(兽人)

StarCraft原本设定为开发周期一年的普通游戏,这样就能赶在1996年圣诞节之前发布。其领导层也是Shattered Nations的领导层。

为了快速发布一款游戏,Blizzard重组了开发团队,大家在两款游戏中间并没有足够的间隙(休息)。

  • Q4 1994 – Warcraft
  • Q4 1995 – Warcraft II
  • Q4 1996 – StarCraft计划发行日期
  • Q2 1998 – StarCraft实际发行日期

现在想想,当初打算快速开发这款游戏真是个荒唐的决定。但公司的董事长Allen Adham受到了增加收入的压力,因为Blizzard早期的游戏太过成功,导致了对其成长的预期过高。

由于时间表太过紧凑,而且员工也有限,StarCraft团队打算开发一款普通的游戏。当时这款游戏被描述为“宇宙中的兽人”,从1996第二季度的E3游戏展中的一幅图片上,我们可以看到当时开发团队的大致路线。

1996年5月,StarCraft在E3上的首次露面

但这时另一个高优先权的游戏出现了——Diablo,这款由加利福尼亚红杉城(Redwood,California)的Condor Studios(秃鹫工作室,后来被称为“北暴雪”)开发的游戏,正需要额外的帮助,而StarCraft开发人员就这样一个接一个地被夺走了。

Condor团队并没有能力制作自己想做的游戏,但其独创的效果非常有趣,这也是Blizzard需要他们的原因。后来Condor被重新命名为Blizzard North(北暴雪),并开始逐渐获得自己应有的投入和员工。

刚开始,我和Collin Murray,一个StarCraft的程序员,飞往Redwood城去帮忙。其它Blizzard“总部”开发人员正在加利福尼亚州Irvine城从事battle.net(战网)网络“供应商”、调制解调器以及局域网游戏,同时还有用户界面屏幕(Blizzard内部称作“glue screens”,用来执行角色创建、加入游戏以及其它游戏功能元素)相关工作。

随着Diablo规模的扩大,Blizzard“总部”所有员工——艺术家、程序员、设计师、音效工程师以及测试人员都转移到了Diablo上,而StarCraft变得无人问津,甚至其团队领导都被指派去处理我没时间完成的游戏安装程序。

在Diablo 1996年发布后,StarCraft又复活了,所有人都有机会了解游戏的发展方向,但它明显前途暗淡。这款游戏已经过时了,给人一种古老的印象,特别当和6个月前在E3展览中大放异彩的Dominion Storm这样的项目相比。

Diablo的巨大成功重塑了Blizzard的目标:StarCraft明确了Blizzard“游戏在完成前不会发布”的战略(这就是“跳票之王的由来”)。但为了证明这一策略,我们遭受了巨大痛苦。

我们需要证明自己

每个人都在用批评性的眼光看待StarCraft,这注定了它比我们之前定义了RTS时代的两款Warcraft还要雄心勃勃!

在StarCraft项目重启时,根据Johnny Wilson——当时最大的游戏杂志Chief of Computer Gaming World的编辑——统计,总共有超过80!!款RTS游戏正在开发中,其中包括现代RTS始祖Westwood Studios。所以我们需要做出一些惊天动地的事!

我们不再是弃儿,因为Warcraft和Diablo的极大成功,我们不可能从游戏和玩家压力下那里得到松懈的机会。在游戏的世界里,你的实力等同你最后一次发挥出来的水准,所以我们需要远远超过之前的辉煌,但这也带来了风险。

新面貌

Warcraft II只有6个核心程序员和两个支持程序员,但对于StarCraft这样的大工程这显然太少了,所以开发团队引入了一些不需要太多的指导就可以编写游戏代码的新人,他们并没有受到足够的训练。

我们的编程领导层也不够可靠:我们并没有意识到给经验不足的开发者足够的指导有多么重要,这样他们可以在项目启动前掌握一些必备的知识,所以这些新学者决定了游戏开发的成败。问题的一大原因在于每个人都疯狂地编程,而没有留下时间去做代码复查、审查以及新人训练。

团队中不仅有很多无经验的成员,StarCraft开发团队的领导也从未给发布了的游戏设计过引擎架构。Bob Fitch有多年的游戏开发经验,并取得了优异的成果,但他一直以来都是在做游戏移植、使用已经完成的引擎,以及为Warcraft I、II做一些特性编程,这都不需要考虑大规模的游戏引擎设计。当他在Shattered Nations做技术领导时,项目又被终止了,所以无法证明他在架构设计上的能力。

开发团队在这个项目上的投入无与伦比,大家牺牲了个人健康和家庭生活来全力以赴。我从未见过在哪个项目上每个人都是这么的热血沸腾地工作着!但项目中的几个关键编程决定给之后的工作带来了很多遗留问题,这也是我将要重点讲述的。

人是物非

在发布了Diablo以及后续清理、打补丁完成后,我开始重新帮助StarCraft项目启动。我没想到面对的将是一个bug的集合体,但事实上这就是我所遇到的。

我本以为能够很容易地返回项目开发中,因为我是如此熟悉其中的代码——其中每个组件我都亲自参与了编码。相反,我震惊地发现引擎的很多组件都被丢弃或者重写了。

游戏的单元类被重头编写了一遍,而单元调度程序被丢弃了。调度器是我编写的每个游戏单元正常工作的保障程序!每个单元都会定期询问:“我把现在的工作完成后应该继续做些什么?”“我应该重新评估路径来获得我将要去的路径吗?”“我已经结束了,应该怎样去清理我自己?”等等。

重写代码会带来很多好处,但删除旧的代码同时也会带来风险。Joel Spolsky在他的文章《你不应该做的事情:一》里说得特别好:

永远记住:当你打算重头开始时,你并没有理由保证能做得比之前更好。首先,你的团队不一定还是当时的团队,所以你们并不一定更有经验。大多数情况,你们只是再一次重蹈覆辙,甚至还可能会带来一些新的问题。

Warcraft的游戏引擎是我们几个月努力的结果,为了增加更多的特性来满足新游戏的需求,一个不成熟的编程团队正打算花很多时间来学习游戏引擎的架构应当如何设计以及为什么。

引擎架构

我使用C语言及Watcom编译器,为Microsoft DOS编写了最初版的Warcraft引擎。为了保证在Microsoft Windows上运行,Bob选择Visual Studio编译器并且用C++重新设计了游戏引擎的架构。这都是可以理解的,但他忽略了一个事实——当时,团队里大多数开发者并不熟悉这门语言,非常容易陷入其陷阱中。

尽管C++很强大,但它很容易被人误用。正如 Bjarne Stroustrup(C++之父)所说,“使用C语言(不当)容易伤到自己的脚,但是C++会让你失去整条腿。”

历史的教训告诉我们,程序员总是会强迫自己在第一个项目中使用新语言的每一个特性,这也就是StarCraft里使用类继承的原因。有经验的程序员在见到游戏单元使用继承链后不可能有什么乐观的想法:

CUnit < CDoodad < CFlingy < CThingy

CThingy对象是可能在游戏地图任何地方出现的图元,但不会移动也没有动作;CFlingys的作用则是创建微粒,当发生爆炸时,会出现多个CFlingys向随机的方向飞去;CDoodad,我记得14年前用的应该是这个名字,是一个未实例化的的类,但它是派生类正常运行的重要保障;而CUnit则是基于CDoodad之上。这些单元的行为在多个模块中非常混乱,而且你想做一件事就必须了解所有的类。

比类层级的混乱更糟糕的是,CUnit类本身是一个定义得非常糟糕的类,使用了多个头文件:

  1. class CUnit ... {
  2. #include "header_1.h"
  3. #include "header_2.h"
  4. #include "header_3.h"
  5. #include "header_4.h"
  6. };

每一个头文件都有数百行,这简直好笑!

很多年后,“优先使用对象组合的”理念才在程序员中得到认可,但StarCraft的的程序员很早就在实践中得到了这一结论。

离发布只剩两个月

因为早期的多难之秋,开发团队重新启动后承受着快速完成的压力,新的时间表显示离产品发布只有两个月。

需要增加的游戏单元和行为,还需要从鸟瞰图(top-down)变成等角图(isometric artwork)、一个全新的地图编辑器、通过battle.net在网上联机等功能,所以即使假设其他开发人员(艺术家、音效师游戏平衡性控制者、测试人员)都没问题,让我们在那么短的时间里完成游戏也是不可能的。但编程部门在接下来的14个礼拜内一直以在两个月内发布游戏为目标!整个团队和Bob一起不停地在增加工作时间:(每周)40小时、42小时、48小时。虽然每个人都投入了巨大的、荒谬的时间,但没有人是真的想自讨苦吃。

我以前经常彻夜编码地开发Warcraft,也有连续一周每天超过14个小时编程完成Diablo的经历,这一切都告诉我彻夜工作没有任何意义。任何代码提交[哈哈,多么乐观的一个词啊]都会在之后发现巨大的问题,然后又需要更多的时间去重写代码。

这么长时间地工作让人变得毫无精神,特别是在做非常需要想象力的工作时,这额外糟糕!所以出现那么多的错误、丢失特性以及极其明显的bug都不足为奇。

顺便提一下,这些疯狂的加班工作并不是强制的——只是因为我们的员工非常想完成杰出的作品。现在想想还真是愚蠢,如果我们能合理地安排时间,一定能完成更好的作品。

目前最令我骄傲的成就是在两年内发布了4个版本的Guild Wars(激战),我也并没有为此带领开发团队夜以继日地工作。

StarCraft游戏崩溃最常见的原因

当我在实现StarCraft的某些重要功能包括战争迷雾、视线、飞行单位路径重叠、语言聊天、AI(人工智能,在游戏中指计算机控制的人工智能单位)增强等等之后,我的主要工作被迫变成了修复bug。等等,语音聊天!在1998年?对,我早在1997年就完成了这一功能!其中使用了第三方的语言转换器,并成功完成了通过网络传递音位、解压,并在其他七位玩家的计算机上播放。

但我们办公室的每一个声卡都需要更新驱动才能保证这一功能真正起作用,而且声卡支持全双工传输(full-duplex)音效(同时录制和播放声音),所以我很遗憾地放弃了这一想法。技术支持的任务会因此变得非常繁重,我们在游戏支持上的投入可能甚至超过从游戏中获得的收入。

无论如何,我修复了很多bug,有的是来自我自己的代码,但更多难懂的问题来自那些疲惫的程序员。几个月前,我收到一封来自Brian Fitzgerald——我有机会共同工作的最优秀的两个程序员之一——的问候,谈到StarCraft的一次代码复查,他们记不清我在整个编程过程中到底做了多少修改以及问题修复,但至少我为此受收到了不少赞扬。

面对整个团队的问题,你可能觉得很难处理一大堆问题。但根据我的经验,StarCraft的最个问题在于双向链接的链表。

链表在新引擎中被广泛用作跟踪行为共享的单位。单位的数目是它前辈的两倍——StarCraft的最大限制是1600,而Warcraft II是800——于是通过将它们在链表中链接在一起,优化特定类型单位搜索是非常重要的。

每个玩家的单位和建筑都有专门的列表。

所有这些列表都被双向链接了,来保证可以经常增加和删除列表中的元素—O(1)—而不必去遍历列表来寻找需要删除的元素—O(N)。可惜的是,每个列表都需要“手动维护”——列表间没有共享链接和解链函数;程序员只能在需要的地方手动内联或者解链。而手动控制代码比使用经过调试的程序更容易出错。

某些链接域(link field)被多个列表共享,所以有必要明确了解对象是链接到哪个列表来保证安全的解链。某些链接域甚至和其它数据类型存储在C union中,将内存使用率降到最低。

因此游戏会一直崩溃,一直崩溃!

为什么要那样做?

悲剧的是,这些链表问题本是可以避免的。和我、Jeff Strain一起创办ArenaNet的Mike O’Brien,写了一个Storm.DLL,Diablo中运行的就是它,使用C++模板实现了双向链表。

在StarCraft开发初期使用的正是那个库,但很快它就被新的开发团队丢掉了,他们开始手动控制链表,只是为了让保存游戏存档变得更简单。

可能这里会让大家有点不明白,我来稍微解释下吧:

保存游戏

在Warcraft之前我玩过的那些游戏的存档功能都很糟糕,相信玩过Origin这款游戏的玩家都会记得存档所花费的时间。当然,当时的硬件跟现在比起来确实要慢得多,但这并不是它并不是存档功能如此糟糕的借口,我决定让Warcraft中不再有同样的问题。

因此,Warcraft使用了一些小技巧将大的内存块一起写入磁盘中,整个单元阵列(600个单元,而每个单元数百字节)会作为一整块写入到磁盘中。所有的非指标(non-pointer-based)全局变量可以一次写入磁盘,包括每一个游戏地形和战争迷雾地图都没有问题。

但奇怪的是,尽管它彻底地简化了代码,但这种整体地保存单位的功能并不是提高游戏保存速度的关键。根本上的原因在于Warcraft中的单位不包括“pointer”数据。

StarCraft的单位,正如之前所提到的,在链表区域中包含很多指针,是完全不同的问题——要保证1600个单位一次写入就必须固定所有链指针(还特别要注意联合指针域),然后再解除所有链指针来继续游戏。Yuck!

给我改回来!

在修复很多很多链表之后,我激烈地讨论要改回去——继续使用Storm的链表,即使这会导致保存游戏的代码变得更为复杂。这里我应该多少提提在Blizzard“讨论”的意思——因为我们年轻、莽撞而又傲慢,所以我们的讨论没有不激烈的,除非已经到了午饭时间,只有这时,才没有人会希望继续讨论下去。

我并没有赢得讨论,因为我们离发布只剩下“两个月”,所以经常对现存的拼凑的系统打补丁,当然也包括修缮引擎。这的确不是什么好方法,也使得我们在接下来的几个月里饱受煎熬,同样深深影响(提高)了我之后写代码的方式,这我将在第二篇里讨论。

其它修缮:StarCraft里的路径搜索(path-finding)

这里我还要再提一个给bug的补丁的故事,这不是在修复潜在问题:StarCraft从鸟瞰图变成了等角图,而背景拼图绘制引擎(background tile-graphics rendering engine),还是用的我在1993/4年所写的,没有做任何改变。

使用方块拼图引擎(square tile engine)绘制等角图并不难,但要保证地图编辑器正常运行可不容易,因为将拼图一块一块贴在一起需要做很多边缘固定,因为地图编辑器会将对角形状的图片绘制在方块中。

图形绘制还不是那么糟糕,在方块拼图上做等角路径搜索却是非常困难。地图没有使用大对角拼图(32×32像素),采用的是8×8的小拼图——这增加了16倍的难度。

那时Brian Fitzgerald当时还不是主程序员,路径搜索问题导致游戏发布变得遥遥无期。路径问题最终成为游戏发布前最后解决的一批问题。我打算就StarCraft路径搜索中众多技术和设计细节单独写一些文章。

结束语

所以你听到我抱怨StarCraft的开发究竟有多难,主要因为公司对于这款游戏方向、开发以及设计等各个层次上的糟糕决定。

我们有幸成为鲁莽但又勇敢的船员,通过自己的才智走过了那些艰苦的日子。最终,我们“处理好了问题”,也停止向其中新增功能并发布这款游戏,玩家永远无法看到下面恐怖的代码。也许这也是编译语言相比JavaScript这样的脚本语言的好处——终端用户永远看不到底层的问题。

在下一篇文章中,我将继续深入技术,并讲述为什么大部分程序员使用错了链表,并提出一个Diablo、battle.net以及Guild War中所使用的替代方案。

如果你不需要链表,我的的解决方案同样适用于更复杂的数据结构,例如哈希表、B-trees以及优先队列。我相信,这些基本思路适用于任何编程。但记住不要试图染指能力外的范围,我将在另一篇文章里讲述为什么。

原文链接:http://www.csdn.net/article/2012-09-13/2809882-tough-times-on-the-road-to-starcraft/1

StarCraft开发的荆棘之路相关推荐

  1. mybatis mapper.xml dtd_全栈开发踩坑之路4-用MyBatis实现服务

    1.前言 上一篇文章介绍了如何设计后端的Mysql数据库:Alex Wang:全栈开发踩坑之路3-MySql数据库设计,本文介绍如何用MyBatis实现后端服务. 本后端项目的Github地址(撰写中 ...

  2. 【CTO讲堂】双创背景下的移动开发及变现之路

    为了帮助IT从业者职业之路拥有更多收获,在诸多C粉的殷切期待下,由CTO俱乐部打造的CTO线上讲堂自登场以来获得大家好评.本期邀请AppCan CTO赵庆华带来双创背景下的移动开发及变现之路的主题分享 ...

  3. 嵌入式开发工程师进阶之路

    嵌入式开发工程师进阶之路 一.从微控制器开始 从微控制器的最小系统入手,以其为核心,选择相应电子元件加上最小系统在面包板或洞洞板上搭建硬件电路,并在嵌入式开发工具下使用C语言编写.编译.连接.链接.调 ...

  4. 【Renesas RA6M4开发板之两路PWM驱动】

    [Renesas RA6M4开发板之两路PWM驱动] 1.0 PWM 简介 1.1 原理 1.2 访问 PWM 设备 2. RT-theard配置 2.1 硬件需求 2.2 软件配置 3. 代码分析 ...

  5. 谈一个普通211计算机研究生学渣的Java后端开发的面试之路

    谈一个普通211计算机研究生学渣的Java后端开发的面试之路 为什么写这篇博客? 2020年是特殊的一年,新冠肺炎肆虐全球,疫情造成的影响远比人们想象的严重,所产生的蝴蝶效应让很多不可能变成可能,奥运 ...

  6. 经验总结--我的小程序开发和进化之路

    前言 从接触小程序开始,到现在大大小小做了差不多有五六个小程序项目了,小项目的只有几个页面,大的项目有几十个页面.此篇文章是对之前项目的一个总结,项目的脚手架,开发框架和后期的优化是一个逐渐进化完善的 ...

  7. java开发cs项目_本硕机械转行cs(java后端开发)上岸之路

    秋招转眼就结束了,将近一年的努力,总算给了自己一个比较满意的结果.写下这篇贴子记录自己的转行以及秋招经历. 其实在转行初期,就无数次幻想着秋招结束,然后写一篇长长的经验贴的那种满足感.下面我尽量把我知 ...

  8. 【经验谈】开发工程师人生之路

    相对同时刚出校门同学从事其它行业而言优厚的薪水,以及不断学习更新的专业知识不仅仅让你感到生活的充实,更满足了你那不让外人知的虚荣心.在刚出校门的 几年中,你经常回头看看被你落在后面的同学们,在内心怜悯 ...

  9. 作为大龄开发人员,敢问路在何方?

    相对同时刚出校门同学从事其它行业而言优厚的薪水,以及不断学习更新的专业知识不仅仅让你感到生活的充实,更满足了你那不让外人知的虚荣心.在刚出 校门的几年中,你经常回头看看被你落在后面的同学们,在内心怜悯 ...

最新文章

  1. python3 import execjs ModuleNotFoundError: No module named ‘execjs‘
  2. iOS 新浪微博-5.2 首页微博列表_转发微博/工具栏
  3. 小心!智能合约再爆高危漏洞,两大加密货币直接变废纸!
  4. ASP.NET中实现复用代码自定义用户控件UserControl的使用
  5. cesium obj转b3dm转换及加载
  6. SCU 4439 Vertex Cover(二分图最小覆盖点)题解
  7. Ubuntu中安装、生成、导入、导出、Python3虚拟环境
  8. PHP 数字转化为自定义长度的字符串[前插后入]
  9. makefile 基础(转)
  10. 利用SMS实现资产管理
  11. 蓝牙RFCOMM协议
  12. c语言cout函数,c++中cin与cout 详解
  13. 疯狂动物城简介第一台通用计算机,疯狂动物城 简介
  14. 每天花半小时给孩子讲故事,把他培养成依赖书的人种
  15. 十 ARM9(2440)的IIC——理论知识及程序实例
  16. 化工机械基础试题及答案
  17. Hibernate3 入门之Api,配置文件详解
  18. MATLAB处理信号得到频谱、相谱、功率谱
  19. 网络系统管理赛项之Debian 九. 2021年网络系统管理项目-模块A--样题(一)
  20. nginxssl证书配置

热门文章

  1. mybatis-plus使用乐观锁插件
  2. Unable to load Maven meta-data from xxx com/github/chrisbanes/photoview/
  3. python脚本文件的扩展命是什么_一些文件的扩展名
  4. 自己的神明——你只能成为自己
  5. Committer identity unknown *** Please tell me who you are...
  6. 学生管理系统(完整版)
  7. 高一计算机考多少及格,高一高二高三,每个阶段考多少分才正常?必读
  8. 5G/NR 物理资源概要
  9. python 获取Jenkins job数据
  10. App中 微信分享 代付功能 业务设计 与 代码实现