Before we plunge headfirst into a pile of patterns, I thought it might help to give you some context about how I think about software architecture and how it applies to games. It may help you understand the rest of this book better. If nothing else, when you get dragged into an argument about how terrible (or awesome) design patterns and software architecture are, it will give you some ammo to use.在我们一头扎进一堆模式之前,我想为你介绍一些关于我如何看待软件架构以及它是如何应用到游戏的一些背景, 这可能对你有帮助。它可以帮助你更好地理解这本书的其余部分。如果不出意外,当你陷入关于设计模式和软件架构是多么糟糕(或者很棒)的一场争论中时,它会给你一些弹药来使用。
note: Note that I didn’t presume which side you’re taking in that fight. Like any arms dealer, I have wares for sale to all combatants. 注解:请注意,我没有假设你站在争论中的哪一方。就像任何军火商一样,我有出售给所有战斗方的武器。
If you read this book cover to cover, you won’t come away knowing the linear algebra behind 3D graphics or the calculus behind game physics. It won’t show you how to alpha-beta prune your AI’s search tree or simulate a room’s reverberation in your audio playback. 如果你从头到尾阅读了这本书,你并不会了解到3D图形背后的线性代数或者游戏物理背后的演算。这本书也不会告诉你如何一步步改进你的AI搜索树或者模拟你的音频播放中的房间混响。
note:Wow, this paragraph would make a terrible ad for the book.
注解:哇,这个段落简直是这本书的一段糟糕的广告。
Instead, this book is about the code between all of that. It’s less about writing code than it is about organizing it. Every program has some organization, even if it’s just “jam the whole thing into main() and see what happens”, so I think it’s more interesting to talk about what makes for good organization. How do we tell a good architecture from a bad one?相反,这本书是关于上面这一切要使用的代码。这里少谈代码,多谈代码组织。每个程序都具有一定的组织性,即使它只是”把所有东西扔到main()
函数里面然后看看会发生什么“,所以我认为讨论如何形成好的组织性会更有趣些。我们如何分辨一个好和坏的架构呢?
I’ve been mulling over this question for about five years. Of course, like you, I have an intuition about good design. We’ve all suffered through codebases so bad, the best you could hope to do for them is take them out back and put them out of their misery.我大概有5年时间一直在思索这个问题。当然,像你一样,我对好的设计有着一种直觉。我们都遭遇过非常糟糕的代码库,最希望的就是带它们出去回来,把它们赶出自己的痛苦。
note:Let’s admit it, most of us are responsible for a few of those.
注解:让我们承认这一点,我们大多数人负责这些中的其中一些。
A lucky few have had the opposite experience, a chance to work with beautifully designed code. The kind of codebase that feels like a perfectly appointed luxury hotel festooned with concierges waiting eagerly on your every whim. What’s the difference between the two?少数幸运的有相反的经验,设计精美的代码工作的机会。那种代码库,感觉就像一个完美的豪华酒店站了很多礼宾在翘首等待你的光临。两者之间有什么区别呢?
For me, good design means that when I make a change, it’s as if the entire program was crafted in anticipation of it. I can solve a task with just a few choice function calls that slot in perfectly, leaving not the slightest ripple on the placid surface of the code.对于我来说,好的设计意味着当我做出一个改动时,就好像整个程序都在期待它一样。我可以只用几个选择函数来完美的调用插槽,而不在代码平静表面留下一丝涟漪。
That sounds pretty, but it’s not exactly actionable. “Just write your code so that changes don’t disturb its placid surface.” Right.‘ 这听起来不错,但是却不完全可操作。”只管写你的代码,这样变化就不会干扰其平静的表面“。正确。
Let me break that down a bit. The first key piece is that architecture is about change. Someone has to be modifying the codebase. If no one is touching the code — whether because it’s perfect and complete or so wretched no one will sully their text editor with it — its design is irrelevant. The measure of a design is how easily it accommodates changes. With no changes, it’s a runner who never leaves the starting line.让我解释下。第一个关键部分是,架构就是关乎着变化。人们不得不修改代码库。如果没人接触代码-不管是因为代码很完美还是完成又或者懒得打开自己的文本编辑器-那么它的设计就是无关紧要的。衡量一个设计的好坏就是应对变化的灵活性。如果没有变化,就像一个跑步者从来没离开过起跑线。
Before you can change the code to add a new feature, to fix a bug, or for whatever reason caused you to fire up your editor, you have to understand what the existing code is doing. You don’t have to know the whole program, of course, but you need to load all of the relevant pieces of it into your primate brain.
在你打开编辑器添加新功能,修复 bug 或者其他原因要修改代码之前,你必须要明白现有的代码在做什么。当然,你不必知道整个程序,但是你需要将所有相关的代码加载到你的大脑中。
note: It’s weird to think that this is literally an OCR process.
这在字面上是一个 OCR 过程,不过这个想法有些奇怪。
We tend to gloss over this step, but it’s often the most time-consuming part of programming. If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves.
我们倾向于略过这一步,但它往往是编程中最耗时的部分。如果您认为从磁盘加载一些数据到 RAM 很慢的话,试着通过视觉神经将这些数据加载到你的大脑里。
Once you’ve got all the right context into your wetware, you think for a bit and figure out your solution. There can be a lot of back and forth here, but often this is relatively straightforward. Once you understand the problem and the parts of the code it touches, the actual coding is sometimes trivial.
一旦你的大脑有了一个全面正确的认识,你稍微思考一下就能提出解决方案。这里可能有很多来回,但通常这是比较直接的。一旦你理解了这个问题和它涉及的代码,实际的编码有时是微不足道的。
You beat your meaty fingers on the keyboard for a while until the right colored lights blink on screen and you’re done, right? Not just yet! Before you write tests and send it off for code review, you often have some cleanup to do.
你的手指在键盘敲一段时间,直到右侧的彩色灯光在屏幕上闪烁时,你就大功告成了,是吗?还没有!在你编写测试,并将它发送给代码审查之前,你经常有一些清理的事情要做。
note: Did I say “tests”? Oh, yes, I did. It’s hard to write unit tests for some game code, but a large fraction of the codebase is perfectly testable.
注解:我说”测试“了吗?哦,是的,我说了。为一些游戏代码编写单元测试比较难,但是大部分代码是可以完全测试的。
I won’t get on a soapbox here, but I’ll ask you to consider doing more automated testing if you aren’t already. Don’t you have better things to do than manually validate stuff over and over again?
注解:我这里不是要慷慨陈词,不过,如果你之前不是的话,我要求你考虑多做自动化测试。难道你没有比一遍一遍手动验证东西更好的事情要做吗?
You jammed a bit more code into your game, but you don’t want the next person to come along to trip over the wrinkles you left throughout the source. Unless the change is minor, there’s usually a bit of reorganization to do to make your new code integrate seamlessly with the rest of the program. If you do it right, the next person to come along won’t be able to tell when any line of code was written.
你在游戏中加入了一些代码,但是你不想后面跟上来的的人被你的代码绊倒。除非变动很小,通常都会做些重新组织工作来让你新加的代码无缝集成到程序中。如果你做的正确,下一个人就不能分辨出每行代码写入的时间。
In short, the flow chart for programming is something like:
简而言之,编程的流程图就像下面这样:
note: The fact that there is no escape from that loop is a little alarming now that I think about it.
注解:现在想想,流程图的环路中没有出口有点小惊悚。
While it isn’t obvious, I think much of software architecture is about that learning phase. Loading code into neurons is so painfully slow that it pays to find strategies to reduce the volume of it. This book has an entire section on decoupling patterns, and a large chunk of Design Patterns is about the same idea.虽然不是很明显,但我认为很多软件架构在关于学习阶段。将代码加载到神经元如此痛苦缓慢,得自己来寻找策略来减少它的体积。这本书有一整章节是关于解耦模式的,许多的设计模式的想法差不多。
You can define “decoupling” a bunch of ways, but I think if two pieces of code are coupled, it means you can’t understand one without understanding the other. If you de-couple them, you can reason about either side independently. That’s great because if only one of those pieces is relevant to your problem, you just need to load it into your monkey brain and not the other half too.你可以用一堆方式来定义”解耦“,但我认为如果两块代码耦合,意味着不理解一方是没法理解另一方的。如果你他们解耦,你可以独立理解任何一方。这很棒因为如果只有一块代码和你的问题相关,你只需要将这块代码装载到你的脑袋中,而不用把另外一段也装载进去。
To me, this is a key goal of software architecture: minimize the amount of knowledge you need to have in-cranium before you can make progress.对我来说,这是软件架构的一个关键目标:在你取得进展前最小化你脑海中需要知识量。
The later stages come into play too, of course. Another definition of decoupling is that a change to one piece of code doesn’t necessitate a change to another. We obviously need to change something, but the less coupling we have, the less that change ripples throughout the rest of the game.后期到来,当然也不可缺少。对解耦的另一个定义就是当改变了一块代码而不必更改另外一块代码。很明显,我们需要更改一些东西,耦合的越轻,更改波及的范围就会越少。
This sounds great, right? Decouple everything and you’ll be able to code like the wind. Each change will mean touching only one or two select methods, and you can dance across the surface of the codebase leaving nary a shadow.这听起来很不错,不是吗?对一切进行解耦,你就可以如风一样编写代码。每一次变化意味着只会涉及到一个或者两个选择方法,然后你就可以在代码库上流水行云,若龙飞,若凤舞。
This feeling is exactly why people get excited about abstraction, modularity, design patterns, and software architecture. A well-architected program really is a joyful experience to work in, and everyone loves being more productive. Good architecture makes a huge difference in productivity. It’s hard to overstate how profound an effect it can have.这种感觉正是为什么人们会为抽象、模块化,设计模式和软件架构感到兴奋。一个架构良好的程序工作起来真的会令人愉悦,每个人都会更加有成效。良好的架构在生产力上会产生巨大的差异。夸大它带来效果如何深远怎么都不为过。
But, like all things in life, it doesn’t come free. Good architecture takes real effort and discipline. Every time you make a change or implement a feature, you have to work hard to integrate it gracefully into the rest of the program. You have to take great care to both organize the code well and keep it organized throughout the thousands of little changes that make up a development cycle.但是,就像生活中的所有的东西一样,它并不是免费的。良好的架构需要很大的努力和准则。每当你做出一个改变或者实现一个功能时,你必须很优雅的将它们融入到程序的其余部分。你必须非常谨慎的来很好的组织代码并能够在开发周期中随着数以千计的小变化,保持组织良好。
note: The second half of this — maintaining your design — deserves special attention. I’ve seen many programs start out beautifully and then die a death of a thousand cuts as programmers add “just one tiny little hack” over and over again.
注解:这小节的下半部分-维护你的设计-需要特别的注意。我曾见到多许多程序在开始时写的很漂亮,但死于一个又一个“一个小补丁而已“。
Like gardening, it’s not enough to put in new plants. You must also weed and prune. 注解:喜欢园艺,只种植是不够的。你必须要除草,修剪。
You have to think about which parts of the program should be decoupled and introduce abstractions at those points. Likewise, you have to determine where extensibility should be engineered in so future changes are easier to make.你必须要考虑程序的哪一部分应该要解耦然后在这些地方引入抽象。同样的,你要确定在哪里做一些扩展性以便将来很容易应对变化。
People get really excited about this. They envision future developers (or just their future self) stepping into the codebase and finding it open-ended, powerful, and just beckoning to be extended. They imagine The One Game Engine To Rule Them All.人们对此非常兴奋。他们设想着,未来的开发者(或者是他们自己)步入代码库,发现代码库开放,强大,只等着被加些扩展。他们想象一个游戏引擎便可统治一切。
But this is where it starts to get tricky. Whenever you add a layer of abstraction or a place where extensibility is supported, you’re speculating that you will need that flexibility later. You’re adding code and complexity to your game that takes time to develop, debug, and maintain.但是,事情就在这里开始变得棘手。当你添加了一个抽象层或者支持可扩展的地方,你猜想到你以后会需要这种灵活性。于是你便为你的游戏增加了代码和复杂性,这需要时间来开发,调试和维护。
That effort pays off if you guess right and end up touching that code later. But predicting the future is hard, and when that modularity doesn’t end up being helpful, it quickly becomes actively harmful. After all, it is more code you have to deal with.功夫不负有心人,如果你猜想对了,最终后来接触到了这部分代码。但是猜测未来是很难的,并且当模块最终没起到作用时,很快它就变得有害。毕竟,你必须处理这些多出来的代码。
note: Some folks coined the term “YAGNI” — You aren’t gonna need it — as a mantra to use to fight this urge to speculate about what your future self may want. 注解:有人杜撰了”YAGNI"一词--你不需要它--作为一个口头禅用来与这个冲动斗争来猜测你的未来的自己会想要什么。
When people get overzealous about this, you get a codebase whose architecture has spiraled out of control. You’ve got interfaces and abstractions everywhere. Plug-in systems, abstract base classes, virtual methods galore, and all sorts of extension points.当人们过分热心关注这点时,你会得到一个架构已经失去控制的代码库。你会看到接口和抽象无处不在。插件系统,抽象基类,虚方法众多,还有各种的扩展点。
It takes you forever to trace through all of that scaffolding to find some real code that does something. When you need to make a change, sure, there’s probably an interface there to help, but good luck finding it. In theory, all of this decoupling means you have less code to understand before you can extend it, but the layers of abstraction themselves end up filling your mental scratch disk.它永远把你带走跟踪通过所有的脚手架发现做了一些实际的代码。当你需要做出改变,当然,有可能是一个接口来帮忙的,但好运气找到它。从理论上讲,这一切的去耦意味着你少来了解之前,你可以扩展它的代码,但抽象层本身最终会填补你的心理暂存盘。
Codebases like this are what turn people against software architecture, and design patterns in particular. It’s easy to get so wrapped up in the code itself that you lose sight of the fact that you’re trying to ship a game. The siren song of extensibility sucks in countless developers who spend years working on an “engine” without ever figuring out what it’s an engine for.像这样的代码库正是让人们反对软件架构,尤其是设计模式。对代码进行包装很容易以至于你忽视了你要推出一款游戏的事实。可扩展性的高歌猛进让无数的开发者花费数年在一个”引擎“上却没有搞清楚引擎究竟是用来做什么的。
There’s another critique of software architecture and abstraction that you hear sometimes, especially in game development: that it hurts your game’s performance. Many patterns that make your code more flexible rely on virtual dispatch, interfaces, pointers, messages, and other mechanisms that all have at least some runtime cost.你有时候会听到关于对软件架构和抽象的另外的批评声,尤其在游戏开发中:这会影响到游戏的性能。许多模式让你的代码更加灵活,但是它依赖于虚拟调度,接口,指针,消息以及其他至少有一些运行成本的机制。
note: One interesting counter-example is templates in C++. Template metaprogramming can sometimes give you the abstraction of interfaces without any penalty at runtime. 注解:一个有趣的范例是 C++ 模板。模板元编程有时可以让你获得接口抽象而没有任何运行时损失。
There’s a spectrum of flexibility here. When you write code to call a concrete method in some class, you’re fixing that class at author time — you’ve hard-coded which class you call into. When you go through a virtual method or interface, the class that gets called isn’t known until runtime. That’s much more flexible but implies some runtime overhead.当你编写在某个类中调用某个具体的方法时,你在编写期间对那个类是确定的--你在硬编码你调用函数的类。当你通过一个虚方法或者接口来实现时,类在运行时才能被确定。这样更具有灵活性,但却会带来运行时开销。
Template metaprogramming is somewhere between the two. There, you make the decision of which class to call at compile time when the template is instantiated.模板元编程是介于两者之间。在模板元编程中,你让模板实例在编译时决定调用时哪个类。
There’s a reason for this. A lot of software architecture is about making your program more flexible. It’s about making it take less effort to change it. That means encoding fewer assumptions in the program. You use interfaces so that your code works with any class that implements it instead of just the one that does today. You use observers and messaging to let two parts of the game talk to each other so that tomorrow, it can easily be three or four.
还有一个原因。很多软件架构的目标是使你的程序更加灵活。讲的是用较少的付出来进行改变。这意味着在程序中编码较少的假设性。你使用接口,以便代码与任何实现接口的类进行工作,而不是一个类。你使用观察者和通信使得游戏的两部分互相沟通,这样明天,它就很容易变成三个或者四个进行沟通。
But performance is all about assumptions. The practice of optimization thrives on concrete limitations. Can we safely assume we’ll never have more than 256 enemies? Great, we can pack an ID into a single byte. Will we only call a method on one concrete type here? Good, we can statically dispatch or inline it. Are all of the entities going to be the same class? Great, we can make a nice contiguous array of them.
但是性能全关乎着假设性。优化的实践会在具体的限制下会做的更好。我们能安全的假设我们永远不会超过256个敌人吗?好极了,我们可以将 ID 打包成一个单字节。我们在这里只会在一个具体类型上调用方法吗?好,我们就静态调度或者对它内联。所有的实体都是同一个类吗?太好了,我们可以将他们做成一个很棒的连续队列。
This doesn’t mean flexibility is bad, though! It lets us change our game quickly, and development speed is absolutely vital for getting to a fun experience. No one, not even Will Wright, can come up with a balanced game design on paper. It demands iteration and experimentation.
这并不意味着灵活性很差!它可以让我们迅速的改变我们的游戏,开发速度是让游戏变得有趣是至关重要的。没有人,即使是 Will Wright, 可以在纸上设计出一个平衡的游戏。这需要迭代和实验。
The faster you can try out ideas and see how they feel, the more you can try and the more likely you are to find something great. Even after you’ve found the right mechanics, you need plenty of time for tuning. A tiny imbalance can wreck the fun of a game.
你越快能够尝试到想法并看看它们的带来的感觉,你就能越多的尝试并越有可能找到一些非常伟大的想法。即便在你已经找到合适的工程师之后,你也要需要充足的时间来进行调整。一个细小的不平衡就会破坏掉游戏的乐趣。
There’s no easy answer here. Making your program more flexible so you can prototype faster will have some performance cost. Likewise, optimizing your code will make it less flexible.
这里没有简单的答案。将你的程序做的更具有灵活性,你便会能够更快速的进行原型编写,将会带来一些性能损失。同样,对你的代码进行优化将会降低它的灵活性。
My experience, though, is that it’s easier to make a fun game fast than it is to make a fast game fun. One compromise is to keep the code flexible until the design settles down and then tear out some of the abstraction later to improve your performance.我的经验,虽然,将一款有趣的游戏做的快速要比将一个快速的游戏做的有趣要简单。一种折衷的办法是保持代码的灵活性,直到设计稳定下来,然后去除一些抽象,以提高游戏的表现。
That brings me to the next point which is that there’s a time and place for different styles of coding. Much of this book is about making maintainable, clean code, so my allegiance is pretty clearly to doing things the “right” way, but there’s value in slapdash code too.这使我想到下一个点是,有一个时间和地点不同风格的编码。本书的很多部分是关于编写可维护,干净的代码的,所以我的意图很清楚,就是用“正确”的方式做事情,但是也存在一些草率的代码。
Writing well-architected code takes careful thought, and that translates to time. Moreso, maintaining a good architecture over the life of a project takes a lot of effort. You have to treat your codebase like a good camper does their campsite: always try to leave it a little better than you found it.编写架构良好的代码需要仔细的思考和花费时间。更多的是,在项目的生命周期内维持一个良好的架构需要很大的努力。你必须把你的代码库看作成一个好的露营者在寻找营地一样:总是试着寻找比已发现的营地更好一点的营地。
This is good when you’re going to be living in and working on that code for a long time. But, like I mentioned earlier, game design requires a lot of experimentation and exploration. Especially early on, it’s common to write code that you know you’ll throw away.当你准备要长期和那份代码打交道时这样是好的。但是,就像我之前提到的,游戏设计需要大量的试验和探索,特别是在早起,编写一些你知道迟早要扔掉的代码是很稀松平常的。
If you just want to find out if some gameplay idea plays right at all, architecting it beautifully means burning more time before you actually get it on screen and get some feedback. If it ends up not working, that time spent making the code elegant goes to waste when you delete it.如果你只是想看看一些游戏想法是否能够正确工作,对其精心设计架构就意味着在真正显示到屏幕和得到反馈之前时间都流逝掉了。如果它最终没有工作,当你删除代码时,花在组织代码使其变得优雅的时间其实都浪费掉了。
Prototyping — slapping together code that’s just barely functional enough to answer a design question — is a perfectly legitimate programming practice. There is a very large caveat, though. If you write throwaway code, you must ensure you’re able to throw it away. I’ve seen bad managers play this game time and time again: 原型-编写代码,其功能比较勉强,只为完成一个设计问题--这样是一个完全合法的编程习惯。特别提醒下,虽然你可以编写一次性的代码,但你必须要确保你能将之扔掉。我不止一次看到一些糟糕的经理这么处理就像这下面这样的场景:
Boss: “Hey, we’ve got this idea that we want to try out. Just a prototype, so don’t feel you need to do it right. How quickly can you slap something together?”老板:”嘿,我们想法已经有了,准备尝试下。只是一个原型,所以不必感到必须要做的正确。大概多久能实现?“
Dev: “Well, if I cut lots of corners, don’t test it, don’t document it, and it has tons of bugs, I can give you some temp code in a few days.”开发:”嗯,如果我简化很多,不测试,不写文档,不管bug,我几天内就可以给你一些临时的代码。“
Boss: “Great!”老板:”太好了!“
A few days pass… 几天后...
Boss: “Hey, that prototype is great. Can you just spend a few hours cleaning it up a bit now and we’ll call it the real thing?”老板:”嘿,原型写的很不错。你能花几个小时清理下代码然后开始真枪实弹的干么?“
You need to make sure the people using the throwaway code understand that even though it kind of looks like it works, it cannot be maintained and must be rewritten. If there’s a chance you’ll end up having to keep it around, you may have to defensively write it well.你需要确保使用一次性代码的人们明白这种一次性代码看起来能够运行,但是它却不可维护,必须被重写。如果可能,你最终可能会保留,但需要你后续修改的特别好。
note: One trick to ensuring your prototype code isn’t obliged to become real code is to write it in a language different from the one your game uses. That way, you have to rewrite it before it can end up in your actual game.有一个小技巧确保你的原型代码不会变成真正的代码,就是使用不同于你游戏使用的语言来编写。这样的话,最终你就必须用你的游戏使用的语言重写一遍了。
We have a few forces in play:开发中我们有几个因素需要考虑:
- We want nice architecture so the code is easier to understand over the lifetime of the project.我们想拥有一个良好的架构这样在项目的生命周期中代码便会更容易理解。
- We want fast runtime performance.我们希望获得快速的运行时性能。
- We want to get today’s features done quickly.我们希望快速完成今天的特性。
note: I think it’s interesting that these are all about some kind of speed: our long-term development speed, the game’s execution speed, and our short-term development speed.
注解:我认为把这些都看成是某种类型的速度蛮有趣的:我们的长期开发速度,游戏的执行速度,以及我们短期内的开发速度。
These goals are at least partially in opposition. Good architecture improves productivity over the long term, but maintaining it means every change requires a little more effort to keep things clean.这些目标至少部分是相对的。好的架构从长远来看,改进了生产力,但维护好的架构就意味着每一个变化都需要更多的努力来保持代码的干净。
The implementation that’s quickest to write is rarely the quickest to run. Instead, optimization takes significant engineering time. Once it’s done, it tends to calcify the codebase: highly optimized code is inflexible and very difficult to change.最快编写的代码实现却很少是最快运行的。相反,优化需要消耗工程时间。一旦完成,也会钙化代码库:高度优化过的代码缺乏灵活性,很难改变。
There’s always pressure to get today’s work done today and worry about everything else tomorrow. But if we cram in features as quickly as we can, our codebase will become a mess of hacks, bugs, and inconsistencies that saps our future productivity.完成今日的工作便担心明天的其他一切总伴随着压力。但是,如果我们尽可能快的完成功能,我们的代码库就会充满了补丁,bug和不一致的混乱,会一点点的消磨掉我们未来的生产力。
There’s no simple answer here, just trade-offs. From the email I get, this disheartens a lot of people. Especially for novices who just want to make a game, it’s intimidating to hear, “There is no right answer, just different flavors of wrong.”这里没有简单的答案,只是权衡下。从我收到的电子邮件中,这让很多人头疼。特别是想做一个游戏的新手们。 听到这样说挺恐吓人的,”没有正确答案,只是错误口味不同。“
But, to me, this is exciting! Look at any field that people dedicate careers to mastering, and in the center you will always find a set of intertwined constraints. After all, if there was an easy answer, everyone would just do that. A field you can master in a week is ultimately boring. You don’t hear of someone’s distinguished career in ditch digging.但是,对于我而言,这令人兴奋!看看人们从事致力的领域,在这中心,你总能找到一组相互交织的约束。毕竟,如果有一个简单的答案,每个人都会这么做。在一周内便可掌握的领域最终是无聊的。你不会接触到在别人的杰出职业生涯中所挖掘出的东西。
note: Maybe you do; I didn’t research that analogy. For all I know, there could be avid ditch digging hobbyists, ditch digging conventions, and a whole subculture around it. Who am I to judge?也许你会;我没有研究这个比喻。据我所知,除非是狂热的挖掘爱好者,沟挖公约,并围绕它的整体亚文化。评价下我?
To me, this has much in common with games themselves. A game like chess can never be mastered because all of the pieces are so perfectly balanced against one another. This means you can spend your life exploring the vast space of viable strategies. A poorly designed game collapses to the one winning tactic played over and over until you get bored and quit.对于我而言,这和游戏本身有很多共同点。就像国际象棋永远无法掌握,因为它是如此完美的平衡。这意味着你可以用尽一生来探索可行的战略空间。设计不当的游戏会用一个稳赢的战术一遍遍玩,直到你厌倦并退出。
Lately, I feel like if there is any method that eases these constraints, it’s simplicity. In my code today, I try very hard to write the cleanest, most direct solution to the problem. The kind of code where after you read it, you understand exactly what it does and can’t imagine any other possible solution. 最近,我觉得如果有任何方法来缓解这些限制,那便是简单性了。在今天我所写的代码中,我非常努力的尝试着编写解决问题最干净,最直接的方法。这种代码在你阅读之后,你会明白它究竟做了什么,并且不敢想象还有其他可能的解决方案。
I aim to get the data structures and algorithms right (in about that order) and then go from there. I find if I can keep things simple, there’s less code overall. That means less code to load into my head in order to change it. 我的目标是保持数据结构和算法的正确性然后继续往下做。我觉得如果我能保持简单性,代码量就会变少。这意味着更改代码时,我脑袋只需装载更少的代码。
It often runs fast because there’s simply not as much overhead and not much code to execute. (This certainly isn’t always the case though. You can pack a lot of looping and recursion in a tiny amount of code.)它通常运行速度快,因为根本就没有那么多的开销,也没有太多的代码要执行。 (这当然并非总是如此。你可以在小部分代码中进行很多的循环和递归。)
However, note that I’m not saying simple code takes less time to write. You’d think it would since you end up with less total code, but a good solution isn’t an accretion of code, it’s a distillation of it.但是,请注意,我并不是说简单的代码会花费较少的时间来编写。你会觉得最终的总代码量更少了,但是一个好的解决方案并不是代码量的减少,而是对代码的一个升华。
note: Blaise Pascal famously ended a letter with, “I would have written a shorter letter, but I did not have the time.” Blaise Pascal的用了一句名言作为了一封信的结尾:“我会写一个更简短的信,但我没有足够的时间。”
Another choice quote comes from Antoine de Saint-Exupery: “Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.”另一种引用来自Antoine de Saint-Exupery:“完美不是不需要再附加什么,而是没有什么从中流逝。”
Closer to home, I’ll note that every time I revise a chapter in this book, it gets shorter. Some chapters are tightened by 20% by the time they’re done.言归正传,我注意到,每次我修改这本书的章节时,它都会变得更短。一些章节会比他们在完成时要缩短20%。
We’re rarely presented with an elegant problem. Instead, it’s a pile of use cases. You want the X to do Y when Z, but W when A, and so on. In other words, a long list of different example behaviors.我们很少会呈现一个优雅的问题。相反,它是一堆用例。你想让当Z的时候让X来处理Y,但当A的时候会W,依次类推。换句话说,是一个不同实例行为的长列表。
The solution that takes the least mental effort is to just code up those use cases one at a time. If you look at novice programmers, that’s what they often do: they churn out reams of conditional logic for each case that popped into their head.即以最少的精神努力解决的办法是只编写了这些用例一次。如果你看一下新手程序员,这是他们经常做的:他们生产出的条件逻辑的里姆斯对于突然出现在他们的头每一种情况下。
But there’s nothing elegant in that, and code in that style tends to fall over when presented with input even slightly different than the examples the coder considered. When we think of elegant solutions, what we often have in mind is a general one: a small bit of logic that still correctly covers a large space of use cases.但没有什么在优雅和代码在风格趋于与输入比编码器被认为是稍有不同的呈现时摔倒。当我们想到优雅的解决方案,就是我们常心目中是一个普遍的一种:逻辑的一个小一点仍然正确占地面积用例空间大。
Finding that is a bit like pattern matching or solving a puzzle. It takes effort to see through the scattering of example use cases to find the hidden order underlying them all. It’s a great feeling when you pull it off.发现有点像模式匹配或解决一个谜。它需要努力识破例如用例的散射,找到隐藏的秩序背后他们。这是一个伟大的感觉,当你把它关闭。
Almost everyone skips the introductory chapters, so I congratulate you on making it this far. I don’t have much in return for your patience, but I’ll offer up a few bits of advice that I hope may be useful to you:
几乎每个人都会跳过介绍章节,所以在这里我祝贺你能够阅读到这里。我没有太多的东西来回报你的这份耐心,但是这里我能给你提供一些建议,希望对你有用:
-
Abstraction and decoupling make evolving your program faster and easier, but don’t waste time doing them unless you’re confident the code in question needs that flexibility.抽象和解耦能够使得你的程序变得更快和更简单点,但不要浪费时间来做这件事除非你确信存在问题的代码需要这种灵活性。
-
Think about and design for performance throughout your development cycle, but put off the low-level, nitty-gritty optimizations that lock assumptions into your code until as late as possible.在你的开发周期中要对性能进行思考和设计,但是要推迟那些低层次的,基本事实的优化,在代码中尽可能晚的锁定住这些假设。
note: Trust me, two months before shipping is not when you want to start worrying about that nagging little “game only runs at 1 FPS” problem. 注解:相信我,在游戏发布前的两个月并不是你开始担心”游戏只跑了1帧“问题的时候。
-
Move quickly to explore your game’s design space, but don’t go so fast that you leave a mess behind you. You’ll have to live with it, after all. 尽快的来探索你的游戏的设计空间,但是不要走的太快留下一个烂摊子给自己。毕竟你将不得不忍受它。
-
If you are going to ditch code, don’t waste time making it pretty. Rock stars trash hotel rooms because they know they’re going to check out the next day.如果你将要删除代码,不要浪费时间来整理让它更整洁。摇滚明星把酒店房间弄的很乱因为他们知道第二天就要结账走人。
-
But, most of all, if you want to make something fun, have fun making it. 但是,最重要的是,若要做一些有趣的玩意,便乐在其中来做吧。