游戏编程模式:前言(架构,性能和游戏)(Part II)

3、性能和速度

    还有另外一个对于软件架构和抽象化的批评你有时候会听到,尤其是在游戏开发领域:就是它们对于游戏的性能有害。很多使得你的代码更加灵活的模式依赖于虚分派(virtual dispatch)、接口、指针、消息等等会增加运行时成本的机制。

    一个有趣的反例是C++中的模板。模板元编程(template metaprogramming)有时候可以在不增加运行时负担的同时为你提供接口的抽象性。

    这里有很多种不同的灵活性(There's a spectrum of flexibility here)。当你写代码来调用某个类中一个具体的方法时,你在创作时间(author time)固定了这个类——你写死了你调用的那个类。当你使用一个虚函数或者接口的时候,直到运行时runtime)才知道调用了哪个类。这个要灵活很多,但是蕴含了一些运行时的负担。

    模板元编程则位于这二者之间。当模板被实例化时,你在编译时compile time)来决定调用哪个类。

        这是有理由的。很多软件架构就是关于将你的游戏变得更加灵活的。这是关于让改变软件更加省力的。这意味着在程序中注入更少的假设。(That means encoding fewer assumptions in the program.)你使用接口,以便你的代码适用于任何实现了该接口的类,而不仅仅是今天管用的那个类。你使用观察者模式(observers)和消息发送(messaging)来让游戏的两个部分互相对话,以便将来可以轻松地应用于三个或四个游戏部分之间的对话。

        但是性能完全是关于假设的。在具体的限制条件之下才能够很好地进行优化的实践。我们能否有把握地假设我们永远不会有超过256个敌人呢?如果是的话,好极了,我们可以将ID打包进单个字节中。我们会不会仅仅对于一个具体的类型来调用一个方法呢?是的话,好的,我们可以静态地分派它,或者将它变成inline函数。所有的实体(entity)都是同一个类的对象吗?好的,我们可以创建一个连续数组来储存它们。

        尽管如此,这并不意味着灵活性是不好的。灵活性让我们迅速地改变游戏,而开发development)速度对于达到一个娱乐体验而言是绝对重要的。没有人,哪怕是Will Wright也好,可以在纸上弄出一个平衡性很好的游戏设计方案。这需要迭代和试验。

        你尝试新主意并观察它们给人的感受的速度越快,你能够尝试的东西就越多,你就越有可能找到给力的东东。甚至是在你找到正确的机制之后,你还是需要大量的时间来进行微调。一点点的不平衡就可以毁掉一个游戏的乐趣。

        没有简单的回答。让你的游戏变得更灵活,以便你可以更快地开发出一个原型,这会带来一些性能的成本。类似地,对你的代码进行优化则会使其灵活性降低。

        但我的经验是,让一个有趣的游戏运行变快,比让一个运行快的游戏变得有趣要难。一种妥协方案是,保持你的代码是灵活的,直到设计方案稳定了,然后拆除掉一些抽象层以提升性能。

 

4、坏代码中的好东西

        这让我想到这样一件事:在有些时机和场合下,是要有不同风格的代码的。本书的很多内容是关于编写可维护的、干净的代码的,所以我很忠诚地拥护用“正确的”方式去做事情,但是草率写就的代码(slapdash code)也是有价值的。

        编写构架良好的代码需要谨慎的考虑,并且这会转换成时间成本。而且,在一个工程的一生中维护一个好的架构需要很大的努力。你得像一个好的露营者对待自己的营地那样对待你的代码库:永远要努力让它处于比你发现它时更好一点的状态(always try to leave ita little better than you found it)。

        如果你打算长期与这代码打交道的话,这做法不错。但是,就像我之前提到的那样,游戏设计需要很多试验和探索。尤其是在早期的时候,你经常会写你知道你将会扔掉的代码。

        如果你仅仅想要知道某个玩法的主意是否管用,将它优美地构架起来意味着在你最终将其显示在屏幕上并得到一些反馈之前要耗费掉更多的时间。如果它最终证明不管用的话,当你删除它时,你用于将你的代码变得优雅所花费的时间就完全浪费掉了。

        原型制造(prototyping)——将一些刚好足够有功能的代码随意地放置在一起以回答一个设计问题——是一个完全正当的编程实践。不过我得给你一个警告。如果你编写了要丢弃的代码,你必须确保你可以将其扔掉。我见过糟糕的经理一次又一次地玩下面的游戏:

    老板:“嘿,我们想出了这个主意,你得试试。只是一个原型而已,所以不要觉得你需要把它做得很对。你多快可以将其组装起来?”

    开发者:“额,如果我走很多近路,不测试,不写文档,并且产生超多的BUG,我可以在几天以内给你一个临时的代码。”

    老板:“好极了!”

几天以后……

    老板:“嘿,那个原型不错。你可以仅仅花费几个小时将其清理清理,然后将它变成正式的?”

        你需要保证使用要扔掉的代码的人理解这一点:即使这代码看起来有点管用,但是它并不能被维护,并且必须被改写。如果If there's a chance you'll end up having to keep it around, you may have to defensively write itwell. (译者注:不会翻译了呀……)

    有一个诀窍可以保证你的原型代码不会被强制地变成正式代码:用跟你的游戏所使用的编程语言不同的语言来编写它。这样的话,在它进入你的游戏之前,你就必须得改写它了。

 

5、达成平衡

        现在场上表演的有这么几个势力:

1、我们想要一个好看的架构,以便代码在工程的整个生命中更容易理解。

2、我们想要快速的运行时性能。

3、我们想要把今天的特性迅速搞定。

    我觉得很有趣:这些都是关于某种速度的:我们长期的开发速度,游戏的执行速度,和我们短期的开发速度。

        这些目标至少是部分矛盾的。好的架构在长期会提升产量,但是维护它意味着每一个更改都需要花费一点更多的努力来保持整洁。

        写起来最快的实现很少是运行起来最快的。相反,优化需要花费很多的工程时间。一旦优化完成,那么它会倾向于将代码库钙化(calcify):高度优化的代码是不灵活的,改变起来很难。

        总是有压力来让今天的工作今日完成,而把其他所有的事情放到明天去担心。但是如果我们尽可能快地填充新特性的话,我们的代码库会变成由hacks,BUG和不一致性所组成的一团乱麻,而这会损坏我们将来的产出。

        这里没有简单的答案,只有权衡。从我收到的电子邮件来看,这让很多人沮丧。尤其是对那些仅仅想要做一个游戏的新手来说,听到这句话是令人生畏的:“没有正确的答案,只有不同口味的错误答案。”

        但是对我来说,这是令人兴奋的!看看任何人们献出其生涯去掌握的领域吧,在中心位置,你总是会找到一个由纠结的约束条件组成的集合。毕竟,如果真的有简单的答案的话,那么每个人就会那样去做了。你一周之内就可以掌握的领域最终是乏味的。你不会听说有人在挖沟(ditch digging)领域中是出类拔萃的。

    也许你确实听说过;我并没有对这个类比进行过调查。就我所知,可能有热切的挖沟业余爱好者、挖沟惯例以及围绕它的一个完整的亚文化。我怎么能够判断呢?

        对于我来说,这与游戏本身有很多共同之处。像国际象棋这样的游戏永远不会被掌握,因为所有的棋子都如此完美地实现了彼此间的平衡。这意味着你可以花上你的一辈子来探索可行的策略所组成的广阔空间。一个设计得糟糕的游戏会坍缩到被使用了一次又一次的必胜策略上,直到你厌倦了并退出游戏。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值