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

本系列文章由@浅墨_毛星云 出品,转载请注明出处。  
文章链接:http://blog.csdn.net/poem_qianmo/article/details/52505170

作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442

一、系列文章前言

承接《代码整洁之道》的精读与演绎,《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 但最重要的是,如果你想要做出让人享受的东西,那就享受做它的过程。

本文就此结束。

With Best Wishes.

时间: 2024-10-11 21:33:18

【游戏设计模式】之一 序言:架构,性能与游戏的相关文章

#游戏编程模式# --- 框架、性能、游戏

---恢复内容开始--- 一.软件框架 1.1 好的软件框架 好的设计意味着当我们做出一个改动时,就好像整个程序都在期待它一样.我们可以调用少量可选的函数来完美地解决一个问题,而不会为软件带来其他的多余的副作用. "只管写我们自己的代码,框架会帮我们收拾一切!". 关键部分:框架意味着变化.衡量一个设计好坏的方法就是看它应对变化的灵活性. 1.2 如何做出改变 好的改变,是在下一个人在添加代码的时候不会察觉我们的代码的变动. 1.3 我们如何从解耦中受益 我们可以用一堆方式来定义&qu

Game Programming Patterns(游戏编程模式)-架构,性能与游戏

游戏编程模式- 架构,性能与游戏 本系列博客是:Game Programming Patterns 的中文翻译版本. 翻译的github地址: cyh24. 如有兴趣,可联系博主共同翻译,一起造(wu)福(dao)他人. 博客虽然水分很足,但是也算是博主的苦劳了, 如需转载,请附上本文链接,不甚感激! 本系列博客 <游戏编程模式>– 目录,可点击进入. 架构,性能与游戏 ============================ 在我们埋头研究一堆的设计模式之前,我想先告诉你,对于软件架构,我个

架构,性能和游戏

在我们一头扎入模式之前,我想先讲一些对软件架构和其在游戏上的应用的理解, 也许能帮你更好的 理解这本书的其余部分. 至少,在你被卷入一场关于软件架构有多么糟糕(或多么优秀) 的辩论时, 这可以给你一些武器支援. 软件架构是什么? 如果你把本书从头到尾读一遍, 你不会找到在3D图形背后的线性代数或者游戏物理背后的微积分. 这书不会告诉你如何用α-β修剪你的AI树,也不会告诉你如何在音频中模拟房间中的混响. 相反,这本书告诉你在程序之间的事情. 与其说这本书是关于如何写代码.不如说是关于如何组织代码

【游戏设计模式】之四 《游戏编程模式》读书笔记:全书内容梗概总结

本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://blog.csdn.net/poem_qianmo/article/details/53240330 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 本文的Github版本:QianMo/Reading-Notes/<游戏编程模式>读书笔记 这是一篇超过万字读书笔记,总结了<游戏编程模式>一书中所有章节与内容的知识梗概. 我们知道,游戏行业其实一直很缺一本系

游戏设计模式——面向数据编程(新)

目录 面向数据编程是什么? 单指令流多数据流(SIMD) 什么是SIMD 为什么需要SIMD 支持SIMD技术的指令集 使用SIMD编程 使用汇编内联 使用指令集库 使用ISPC语言 并行循环 避免Gather行为 CPU缓存(CPU cache) 什么是CPU缓存 为什么需要CPU缓存 CPU缓存预先存的是什么 CPU缓存命中/未命中 提高CPU缓存命中率 使用连续数组存储要批处理的对象 避免无效数据夹杂在连续内存区域 冷数据/热数据分割 频繁调用的函数尽可能不要做成虚函数 重新认识C++ S

一款成功的全球服游戏该如何进行架构选型与设计?

全球服游戏如今正在成为出海游戏的主要考虑模式,跨国对战.全球通服打破国界的限制,将不同地区不同语言的玩家放在一起合作/竞技,成功吸引了大量玩家的关注,并逐渐成为主流的游戏玩法. 游戏厂商们也在尝试采用一地部署多地覆盖.全球逐步宣发的模式进行第一款全球服游戏的发行试点及成本控制.文章主要针对全球服和出海游戏的特性优势.架构布局.网络规划.实用技术等几个方面进行探讨. 本文主要观点: (1) 微服务是主流,模块化架构易于扩容以及维护,微服务+自动化的业务架构对于全球服游戏来说几乎是必然的选择: (2

游戏服务器之多进程架构通信 协程切换只是简单地改变执行函数栈,不涉及内核态与用户态转化,也涉及上下文切换,

游戏服务器之多进程架构通信 https://gameinstitute.qq.com/community/detail/124098 https://www.zhihu.com/question/23508968 游戏服务器与普通服务器有什么区别? 游戏开发中的TCP.UDP.HTTP.WebSocket四种网络通讯协议对比 https://gameinstitute.qq.com/community/detail/127562 https://www.jianshu.com/p/4eb37c1

【游戏设计模式】之二 实现撤消重做、回放系统的神器:命令模式

本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://blog.csdn.net/poem_qianmo/article/details/52663057 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 这篇文章将与大家一起探索游戏开发中命令模式的用法. 命令模式的成名应用是实现诸如撤消,重做,回放,时间倒流之类的功能.如果你想知道<Dota2>中的观战系统.<魔兽争霸3>中的录像系统.<守望先锋>

《C++ Qt 设计模式》8|15拼图 小游戏的简单实现。拜托,别乱点!

第零章:介绍 看到这个游戏了,感觉蛮好玩的,实现了一下. 界面如下: 游戏玩法:在3×*3的矩阵中,每个按钮都可以点击,如果按钮四周有一个是空白,则点击此按钮则会移动到这个空白.按钮字母顺序变成“ABCD……”这样有序就赢了,最后空格可以出现在任何地方. 第一章:构思 设计模式基本上没接触过,所以就没有按书上的方式,自己想了大概要怎么实现,可能自己像的没有它给出的方式好吧,但是毕竟是菜鸟嘛,一步一步来! 1.用什么装这些按钮 学习了QGridLayOut,“The QGridLayout cla