游戏设计模式】之一 序言:架构,性能与游戏

转载自:https://blog.csdn.net/potato47/article/details/53463916

一、系列文章前言

 

 

承接《代码整洁之道》的精读与演绎,《Game Programming Patterns》是我们下一个目标。

 

这个系列的诞生,是因为最近闲暇时一直在阅读一些之前已经列入待看书单的经典著作,并有将阅读过程中一些思考和总结写成文字进行记录。为了不枉费这些阅读、思考与总结的过程,决定将这些零散的内容整理成文,并集合成系列,将他们系统地记录下来,也希望能对热爱编程的各位有所帮助。

 

需要说明的是,虽说这些文章的原型是读书笔记,但会采用和传统读书笔记不一样的方式。他们不会仅仅停留在传达作者含义的阶段,而会是对一本书核心内容的全新演绎,内容解刨,精炼与解读。与其说是读书笔记,不妨说是原著经过的解读后的通俗版本。

二、《Game Programming Patterns》其书

 

 

 

《Game ProgrammingPatterns》,正如其名,是一本专注于游戏编程领域的设计模式指南,它涵盖了游戏逻辑,游戏编辑器,和游戏引擎的编程中的常用技法。作者Robert Nystrom有二十年的从业经验,在EA工作8年。

“这本书将游戏开发中经常涉及到的编程模式拎出来,结合具体开发中遇到的实例一步步的引出对应的模式,这比起四人帮的《设计模式》更加具体。”

 

不同于传统的出版方式,这本书是网络出版,然后Web版完全免费,其更是在amazon上具有罕见的5星评价,可见读者对其的好评程度之高。加之书中内容生动有趣,将各种经验之谈娓娓道来,实在是业界良心。

书本主页:http://gameprogrammingpatterns.com/

Web版全文阅读:http://gameprogrammingpatterns.com/contents.html

三、本文涉及知识点思维导图

 

 

先放出这篇文章所涉及内容知识点的一张思维导图,就开始正文。大家若是疲于阅读文章正文,直接看这张图,也是可以Get到本文的主要知识点的大概。

 

 

 

 

 

四、何为好的软件架构

 

 

《Game ProgrammingPatterns》一书中说,好的设计意味着当我改了点什么, 整个程序就好像正在等着这种改动。我们可以加入几个函数调用完成任务,同时丝毫不改变代码平静表面下的脉动。

这听起来很酷,只是实行起来很难。“把代码写到改变不会影响其平静表面。”若真能做到,确实不错。

这样太理想化了,还是让我们通俗些吧。架构是有关于变化的,让我们拥抱变化,从变化开始入手。总有人改动代码。如果没人碰代码,无论是因为代码至善至美,还是糟糕透顶,那么它的架构设计就毫无意义。评价架构设计就是评价它应对变化有多么轻松。没有了变化,它就是永远不会离开起跑线的运动员。

轻松应对变化,这就是好的软件架构的主要优点之一。

 

 

 

 

五、一个新特性的实现过程

 

 

在你改变代码去添加新特性,去修复漏洞,或者随便什么需要使用编辑器的时候, 你需要理解现在的代码在做些什么。当然,你不需要理解整个程序,但你需要将所有相关的东西装进你的灵长类大脑。

我们通常无视了这步,但这往往是编程中最耗时的部分。 如果你认为将数据从磁盘上分页到RAM上很慢, 那么试着通过一对神经纤维将数据分页到大脑中。

一旦把所有正确的上下文都记到了你的大脑里, 想一会,然后找到解决方案。 这可能会有来回打转的时刻,但通常比较简单。一旦你理解了问题和需要改动的代码,实际的编码工作就很容易了。

你将一些代码加入了游戏,但不想下一个人被你留下来的小问题绊倒。 除非改动很小,否则就还需要一些工作去微调新代码,使之无缝对接到程序的其他部分。如果做对了,那么下个见到代码的人甚至无法说出哪些代码是新加入的。

 

简而言之,编程的流程图看起来是这样的:

 

      

 

 

PS:看起来,这是一个令不少程序员听之色变的死循环:)

 

六、解耦与学习阶段

 

 

其实,很多软件架构都和学习阶段(learning phase)息息相关。 将代码载入到神经元太过缓慢,找些策略减少载入的总量是很值得做的事。GPP(以后不妨将《Game ProgrammingPatterns》一书简称为GPP)一书中有整整一章是关于解耦模式(decoupling patterns), 还有很多常规的设计模式也牵扯到了解耦。

可以用多种方式定义“解耦”,这边是其中之一的理解方式:

如果有两块代码是耦合的, 那就意味着无法仅仅只理解了其中一个,而对另一个丝毫不了解。如果解耦了他俩,就可以独自的理解其中之一,根本无需牵扯到另一个。

GPP一书中说道,我所理解的软件架构的关键目标,就是最小化在处理前需要进入大脑的知识。

当然,也可以从后期阶段来看。 那么,另一种解耦的定义是:当一块代码有变化时,没必要修改另外的代码。 肯定需要修改一些东西,但耦合程度越小,变化会波及的范围就越小。

 

 

 

 

 

七、过度设计的代价

 

 

首先,来一个设想。

只要解耦掉任何内容,然后,风烟俱净,天山共色,从流飘荡,任意东西,就可以像风一样写代码。每个变化都只修改一两个特定方法,万花丛中过,片叶不沾身,这是多么的惬意,是吧?

这大概就是人们对抽象,模块化,设计模式和软件架构兴奋的原因。在有好架构的程序上工作是很好的体验,每个人都希望能更有效率地工作。好架构能造成生产力上巨大不同。很难再夸大它那强力的影响。

 

但是,就像生活中的任何事物一样,没有免费的午餐。好的设计需要汗水和纪律。 每次做出改动或是实现特性,你都需要将它优雅的集成到程序的其他部分。需要花费大量的努力去管理代码, 在开发过程中面对数千次变化仍然保持它的管理结构。

我们会看到无数程序有个优雅的开始,然后死于程序员一遍又一遍添加的“微小黑魔法”。就像园艺,仅仅增加新植物是不够的,还需要除草和修剪。

你得考虑程序的哪部分需要解耦,然后再引入抽象。同样,你需要决定哪部分要设计得支持插件来方便未来的变化。(所谓的面向未来编程)。

人们对这点变得狂热。他们设想以后的开发者(或者只是未来的他们自己)进入代码库,并发现它极为开放,功能强大,极具扩展性,他们惊叹“有此游戏引擎,夫复何求”。

 

当过分关注这点时,你会得到失控的代码库。 接口和抽象无处不在。插件系统,抽象基类,虚方法,还有各种各样的扩展点。

当需求变更时,有可能某个接口能帮上忙,但能不能找到就只能祝你好运了。 理论上,解耦意味着在修改代码之前需要了解的代码更少,但其实你需要对抽象层有很多的了解。

但是,还是那句话,理想很丰满,现实很骨感。 每当你添加了一层抽象或者支持扩展的部分,其实就是在赌这部分功能以后是否用得上。 添加代码和复杂性到游戏中,这都需要时间来开发,调试和维护。如果你猜对了,后来使用了这些代码,那么功夫不负有心人。 但预测未来很难,如果模块化最终无益,那就有害。 毕竟,你得花时间去实现这些代码。有些人喜欢简写为术语“YAGNI”——You aren't gonna need it(你不需要那个)——来对抗预测将来需求的强烈欲望。

 

过度去关注设计模式和软件架构,会让一批人很容易地沉浸在代码中,而忽略要自己的最终目的是要发布游戏。无数的开发者听着加强可扩展性的“警世名言”,花费多年时间制作“引擎”, 却没有搞清楚做引擎是为了什么。

 

 

 

 

八、性能与速度

 

 

软件架构和抽象有时会被批评,尤其是在游戏开发中: 它伤害了游戏的性能。 许多让代码更灵活的模式依靠虚拟调度、 接口、 指针、 消息,和其他机制, 而这些都会消耗运行时成本。

 

一个有趣的反面例子是C++中的模板。模板编程有时可以给你抽象接口而无需运行时开销。

这是灵活性的两极。当写代码调用类中的具体方法时,你已经硬编码了调用的是哪个类。但通过虚方法或接口,直到运行时才知道调用的类。这样更加灵活,但增加了运行时开销。

模板编程是在两者之间。在编译时初始化模板,决定调用哪些类。

 

还有一个原因。很多软件架构的目的是使程序更加灵活。 这让改变它需要较少的努力。编码时对程序有更少的假设。你可以使用接口,让代码可与任何实现它的类交互,而不仅仅是现在写的类。灵活性可以让我们快速改进游戏。

 

让你的程序更加灵活,在损失一点点性能的前提下更快地做出原型。 但需要注意,优化现有的代码可能会让代码丧失原有的灵活性。

而一种折中的办法是保持代码灵活直到设计定下来,再抽出抽象层来提高性能。

 

 

 

 

九、烂代码在原型阶段的优势

 

 

野百合也有春天,之前在《代码整洁之道》中被我们吐槽的烂代码,其实也有它们的优势——快。

我们知道,编写良好架构的代码需要仔细地思考,这会转为时间上的代价。 在项目的整个周期中保持良好的架构需要花费大量的努力。 你需要像露营者处理营地一样小心处理代码库:总是保持其优于你刚刚接触它的时候。就像我们之前《代码整洁之道》系列文章第一篇中说到的:让代码比你来时更干净。

当你要在项目上花费很久时间的话,保持编写良好架构的代码的习惯,是非常值得推崇的。但你知道,游戏开发需要很多实验、探索与试错。 特别是在早期,写一些你知道要扔掉的代码是很普遍的事情。

而如果只想试试游戏的某些主意是不是正确的, 良好的设计意味着在屏幕上看到和获取反馈之前要消耗很长时间。如果最后证明这点子不对,那么删除代码时,你花费的那些为了让代码更加优雅的额外时间,就白费了。

 

但你得让人们清楚,可抛弃的代码即使看上去能工作,也不能被维护,必须重写。如果有可能要维护这段代码,就得防御性好好编写它。

一个保证原型代码不会变成真正使用的代码的技巧是使用和正式游戏不同的编程语言。这样,在实际应用于正式游戏中之前必须重写。

 

在原型开发阶段,能尽快让你做出原型产品,最终让产品成功上线的最初的功臣,或许就是设计糟糕的烂代码。因为他们实现想法够快,不需要缜密的设计与架构。只是这些烂代码在经历了原型设计阶段之后,一定要被重写或者重构。

 

 

 

 

十、开发周期中因素的动态平衡

 

 

在整个开发周期中,如下三大要素一直在相互角力:

 

    1. 为了在项目的整个生命周期保持其可读性,我们需要好架构。
    2. 需要更好的运行时性能。
    3. 需要让现在的特性更快的实现。

 

有趣的是,这三点都是速度:长期开发的速度,游戏运行的速度,和短期开发的速度。

 

这些目标至少是部分对立的。 好架构长期来看提高了生产力, 也意味着维护每个变化都需要更多努力让代码保持整洁。

实现起来最快的代码很少是运行时最快的。 相反,提升性能需要很多的编程时间。而且一旦完成,它就会污染代码库:高度优化的代码不灵活,很难改动。

总有今日事今日毕的压力。但是如果尽可能快地实现特性,代码库就会充满黑魔法,漏洞和混乱,阻碍未来的产出。

 

对于这个三者的权衡,没有简单明了的解决方案,只有具体问题具体分析,按实际的项目状况去去权衡,让三者保持友好的动态平衡,让整个项目保持良好的状态。

 

 

 

十一、本文涉及知识点提炼整理

 

 

本文涉及知识点提炼整理,一些关于游戏架构与性能的心得总结:

1 抽象和解耦会让代码的扩展性和灵活性更加强,但会花费额外的实现时间。除非你觉得这样的灵活性有必要,否则没必要过度的去追求。

2 性能优化很重要,但是要注意时机。在整个开发周期中,最好先专注于实现基本需求,把那些可能限制到项目进度的性能优化尽量延后。

3 在整个开发周期中,灵活性和高性能往往不能兼得。我们可以保持代码的灵活性直到设计定下来,再抽出抽象层来提高性能。

4 在原型开发阶段,能尽快让你做出原型产品,最终让产品成功上线的最初的功臣,或许就是设计糟糕的烂代码。因为他们实现想法够快,不需要缜密的设计与架构。只是这些烂代码在经历了原型设计阶段之后,一定要被重写或者重构。

5 如果打算抛弃这段代码,就不要尝试将其写完美。“摇滚明星将旅店房间弄得一团糟,因为他们知道明天会有人来打扫干净。”

6 提倡去写出最简单,最直接的整洁代码。你读过这种代码后,完全理解了它在做什么,想不出其他完成的方法。“完美是可达到的,不是没有东西可以添加的时候,而是没有东西可以删除的时候。”

7 但最重要的是,如果你想要做出让人享受的东西,那就享受做它的过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据来源:中经数据库 主要指标110多个(全部都是纯粹的 市辖区 指标),大致是: GDP GDP增速 第一产业增加值占GDP比重 第二产业增加值占GDP比重 第三产业增加值占GDP比重 人均GDP 社会消费品零售总额 固定资产投资(不含农户) 新设外商投资企业数_外商直接投资 实际利用外资金额(美元) 一般公共预算收入 一般公共预算支出 一般公共预算支出_教育 一般公共预算支出_科学技术 金融机构人民币各项存款余额_个人储蓄存款 金融机构人民币各项存款余额 金融机构人民币各项贷款余额 规模以上工业企业单位数 规模以上工业企业单位数_内资企业 规模以上工业企业单位数_港澳台商投资企业 规模以上工业企业单位数_外商投资企业 规模以上工业总产值 规模以上工业总产值_内资企业 规模以上工业总产值_港澳台商投资企业 规模以上工业总产值_外商投资企业 规模以上工业企业流动资产合计 规模以上工业企业固定资产合计 规模以上工业企业利润总额 规模以上工业企业应交增值税 规模以上工业企业主营业务税金及附加 户籍人口数 年均户籍人口数 户籍人口自然增长率 第一产业就业人员占全部城镇单位就业人员比重 第二产业就业人员占全部城镇单位就业人员比重 第三产业就业人员占全部城镇单位就业人员比重 城镇非私营单位就业人员数 城镇非私营单位就业人员数_第一产业 城镇非私营单位就业人员数_第二产业 城镇非私营单位就业人员数_第三产业 城镇非私营单位就业人员数_农、林、牧、渔业 城镇非私营单位就业人员数_采矿业 城镇非私营单位就业人员数_制造业 城镇非私营单位就业人员数_电力、热力、燃气及水生产和供应业 城镇非私营单位就业人员数_建筑业 城镇非私营单位就业人员数_批发和零售业 城镇非私营单位就业人员数_交通运输、仓储和邮政业 城镇非私营单位就业人员数_住宿和餐饮业 城镇非私营单位就业人员数_信息传输、软件和信息技术服务业 城镇非私营单位就业人员数_金融业 城镇非私营单位就业人员数_房地产业 城镇非私营单位就业人员数_租赁和商务服务业 城镇非私营单位就业人员数_科学研究和技术服务业 城镇非私营单位就业人员数_水利、环境和公共设施管理业 城镇非私营单位就业人员数_居民服务、修理和其他服务业 城镇非私营单位就业人员数_教育 城镇非私营单位就业人员数_卫生和社会工作 城镇非私营单位就业人员数_文化、体育和娱乐业 城镇非私营单位就业人员数_公共管理、社会保障和社会组织 城镇非私营单位在岗职工平均人数 城镇就业人员数_私营企业和个体 城镇非私营单位在岗职工工资总额 城镇非私营单位在岗职工平均工资 城镇登记失业人员数 建成区面积 建设用地面积 建设用地面积_居住用地 液化石油气供气总量 液化石油气供气总量_居民家庭 人工煤气、天然气供气总量 人工煤气、天然气供气总量_居民家庭 液化石油气用气人口 人工煤气、天然气用气人口 城市公共汽电车运营车辆数 城市出租汽车运营车辆数 城市公共汽电车客运总量 道路面积 排水管道长度 建成区绿化覆盖面积 建成区绿化覆盖率 绿地面积 公园绿地面积 维护建设资金支出 土地面积 生活用水供水量 供水总量 全社会用电量 城乡居民生活用电量 工业生产用电量 房地产开发投资 房地产开发投资_住宅 限额以上批发和零售业法人单位数 限额以上批发和零售业商品销售总额 普通中学学校数 中等职业教育学校数 普通小学学校数 普通高等学校专任教师数 普通中学专任教师数 中等职业教育专任教师数 普通小学专任教师数 普通高等学校在校生数 普通中学在校生数 中等职业教育在校生数 普通小学在校生数 电视节目综合人口覆盖率 公共图书馆总藏量_图书 医疗卫生机构数_医院和卫生院 卫生人员数_执业(助理)医师 医疗卫生机构床位数_医院和卫生院 城镇职工基本养老保险参保人数 职工基本医疗保险参保人数 失业保险参保人数

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值