Unfortunately, for the past 18 years I have seen repeatedly (in the game industry and outside) that they tend to be used as shopping lists by developers who can't (or don't have time to) think, design and architect properly. The result of such approach to development, let's call it Pattern Oriented Design, are bloated, overengineered and underperforming systems that are still harder to write, document, test and evolve, than the disorganized messes they replaced.
So, nice work, but beware programmers: you must first understand the problem you are trying to solve, before you go shopping for a solution.
I'm currently working on a paper trying to analyze the X-Com remake through design patterns, and it seems to be working reasonably well so far. The pattern language gives some hooks for talking about what specifically was kept or changed in the remake. However, for that project we're using more design-oriented rather than programming-oriented patterns, from this project: http://gdp2.tii.se/
(There used to be a book, but it's out of print: http://www.amazon.com/gp/product/1584503548/ref=as_li_ss_tl?...)
You have a problem, then you realize, "I think this is what the x pattern is for. How does that pattern work, again?" You then google it and try to implement it correctly. Almost by definition, you're not implementing the most obvious solution. If you were, you probably wouldn't have to google it. It'd be better to implement the simplest solution that could possibly work.
I think design patterns may be good as a communication tool, and no more. It's faster to say, "factory" than to describe the pattern's implementation. But code maintainability is way higher priority than using a pattern and in my experience pattern implementation and code maintainability are mutually exclusive.
I developed games as a kid for many years, and I developed solutions to some problems that I identified later as patterns while reading the GoF book.
However, if I had read the GoF book in the first place and tried to forcefully implement patterns just based on reading about them in a book, I'd have done a miserable job.
Almost by definition, you're not implementing the most
obvious solution. If you were, you probably wouldn't have
to google it
If you look at the chapter outline, I'm planning to have an introductory section on "How to Use Patterns (and How Not to Abuse Them)" where I'll try to mitigate this. Also, each chapter has a "Keep in Mind" section that focuses on telling you when a pattern might not be a good fit.
"I started trying to find books about how to organize programs. Fast-forward several years and a friend hands me a new book: Design Patterns: Elements of Reusable Object-Oriented Software. Finally! The book I’d been looking for since I was a teenager."
"But the architecture that this brilliant code hung from was often an afterthought. [...] if they ever cracked open Design Patterns at all, never got past Singleton."
I'm sure most cases of badly architected code you have ran into were caused by insufficient time and/or attention devoted to design and architecture. Now the question is, do you think if these coders had the same time/attention but knew patterns, the result would be better? How would it compare to these same coders without patterns but with more time/attention?
In that sense, metaphor alert a book of design patterns is to programming what a dictionary is to writing: useful, but mostly orthogonal to the quality of the output.
Yes, I do. I've seen a number of times where a smart developer burned hours half-way reinventing from scratch some crude architectural pattern that they could have spent five minutes looking up in a book. Worse, if they had looked it up, they may have learned some of the documented pitfalls that come with it instead of stumbling right into them.
I agree there are few things worse than a codebase jam-packed with inappropriate design patterns. Unfortunately, one of the few things that is worse is a code base where the programmers have clearly never learned about any design patterns at all.
A lot of people seem to think, for instance, that the GoF design patterns are the only design patterns. They just memorize the list, but fail to understand when and why (not) to use them. To me, any frequently employed construction is a design pattern, including but not limited to the classical examples.
The GoF emphatically agrees with you. They considered their book a starting point, not the final word.
I'd say: learn the patterns, and then forget about them.
And when you'll work on an actual problem, the one pattern that is relevant to your problem will come naturally as a solution.
It's used for multiplayer synchronization. Instead of sending thousands of objects, it's enough to send mouse clicks and keystrokes.
"1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond" http://www.gamasutra.com/view/feature/3094/1500_archers_on_a...
It's also used for game replays.
I wrote a 99% deterministic physics system for the ball trajectory during shots and passes, which gave the ball an incredibly natural behaviour. When a shot was taken, the system simulated up until the ball went below the rim's height, so it knew if the shot would hit or miss, and where was the best position to go for the rebound.
Once in a while the prediction would fail. Determinism is freaking hard, but luckily the game didn't rely on it for networking or anything else.
While debugging I also made the experiment of feeding this into the announcers and color commentary. The result was funny and disturbing in equal parts, definitely not a good idea.
- double result = x * y * z;
+ double tmp = x * y;
+ double result = tmp * z;
(disclaimer: second hand war story from OpenTTD, might not be entirely accurate)
Note that fixed-point arithmetics is almost identical to integer arithmetics, thus easy to implement. If you define your physical units small enough (e.g. velocity as pixels/ms instead of pixels/s), then you can use integer arithmetics right away without any confusion.
One minor drawback is that you don't have easy access to cos(), sin(), sqrt(), etc. without going through floating point. However, you usually don't need those in the game engine anyway. For instance, instead of "sqrt(xx + yy) < r", just write "xx + yy < r*r".
The major drawback is that you now might have to deal with integer overflows (and underflows). You should deal with them in floating point, too, but these are more unlikely there. One might think that this won't destroy determinism if you are using e.g. always 32 bit integers on all platforms, but note that some kinds of (signed) underflows or overflows are technically undefined, at least in the C standard, so some compilers might do optimizations that lead to different underflow/overflow results than expected.
> "I work at Gas Powered Games and i can tell you first hand that floating point math is deterministic. You just need the same instruction set and compiler and of course the user’s processor adheres to the IEEE754 standard, which includes all of our PC and 360 customers. The engine that runs DemiGod, Supreme Commander 1 and 2 rely upon the IEEE754 standard."
There seem to be conflicting opinions from game developers about whether you can coax determinism out of IEEE 754 floating point, if you're careful about it. From what I've read, the IEEE 754-2008 revision is supposed to take the goal of reproducible results more seriously as well, but I'm not sure how widely implemented or reliable its efforts in that department are.
But it always worked on my computer!
One of the biggest flaws when I was brought in, is that each avatar (unit/man/truck/etc) was setup with it's own thread that handled its' own move event timer. The issue was that a few dozen avatars in a simulation combined with dozens of simulations on a server would lead to thousands of threads each causing memory swaps, with the need for locking to avoid race conditions and other contention issues.
It wasn't a real-time interface, so a second plus or minus to responses wasn't a huge deal. I refactored the avatars to work of a central loop. The only place with common interaction was a message queue for inbound/outbound messages to the various clients to the engine instances.
This cut down on a lot of the cpu contention and similar issues. This would have been less of an issue for something simple on a single computer, but running many such simulations on a single computer, you come into different problems.
It was actually fun to work around this.
But I can see how singletons get out of hand, but could also make sense to the developer to do so. (Meaning the developer thinks it makes sense to over use when it might not)
How do you approach the fact that maybe your game scene, layers on that scene, HUDS, all would just have a single instance ever?
I have read that singletons are used a lot in games and well games are playable (meaning they run and work) so do you ever apply "if it ain't broke" philosophy?
Your site will be very useful to a lot of people. Singleton alone.
In Unity 3D they work well because the component attachments are driven by the visual interface. Your coding happens inside components, and is then linked up in the scene visually.
The second game engine I implemented used an entity component system, and I found myself not wanting to use it. Attaching and building components in code just to do trivial things was tiresome.
I think such a system is valuable for a game that's already designed, prototyped and ready to be implemented cleanly, but it's not a great pattern for prototyping/rapid game development.
It's almost like you need one set of patterns for game prototyping (messy, hacky, and immediate operations), and an orthogonal set of patterns for final game implementation.
1. Saving game state is trivial (as is replaying it, like in a rewind or time-travel)
2. For garbage-collected environments, it's easy to completely avoid GC pauses by re-using old game state objects.
3. Adding existing functionality to any object is trivial.
#2 was the biggest win for me. I made some small bullet-hell style shoot-em-ups in C# that would pause for GC until I reworked the game into a component system where all game objects were stored in a table, and each row in the table contained all columns for all possible objects properties.
So when a bullet went offscreen, the row its state was stored in became available for the next game object (bullet, alien, etc.) The only step necessary was to wire that row to the various components that would control the game object. The GC pauses disappeared, and this made changing the game and prototyping extremely easy. I'd imagine this would simplify memory management in C++ in the same manner.
I also really enjoyed have things like "AffectedByGravity" as a component and the ability add objects arbitrarily into it, instead of some hackish inheritance chain.
In my case it involved adding the transform component, the sprite render component, any movement components (usually custom subclasses for the specific game) and then adding that node into the graph.
When I wanted to hack away quickly on ideas I found I avoided my own engine because of all the typing it took just to get something up on the screen and moving. Or I found myself creating simple wrappers on top of the component system that killed the flexibility for the sake of brevity and readability.
In my personal experience coding with components did something to stop the creative, messy part of my brain that just wanted to see things on the screen and moving as soon as possible.
I think most implementations mix and match some sort of OO and that would get messy quick. What I did worked great for the particular bullet-hell style shmup I was making. I don't know how well it would work with other types of games, particularly those with game entities that had a ton of state that wasn't shared.
With the bullet-hell game, an enemy shared a lot of state with the main character and bullets. An RPG might be a different story.
To do this, I have some generic container types that are extensions of common ones(list, bag, etc.) to be aware of entities and clean up after them. Instances of the generics are subsequently parametrized with the data I need. The top level entities hold the minimum of information needed to do despawning.
It's had some success in use, but I'm not ready to say it's a major improvement.
Still, what is has is very useful and I'd suggest giving it a read.
BTW in my game I do some component communication by having a GetComponent method on GameObject that takes a component identifier and returns the component. This is a compromise between messaging and direct reference and I think many engines offer this :)
So please do keep it up :)
I've borrowed many of the game loop and other game programming principles when developing code for controlling external equipment like robots and radio transceivers. It's a better model than some of the crazy state machine code I'd written in the past.
Or, if you're not interested in making some money out of it, maybe turn it into wiki style website, so that other people could chip in and you would moderate the submitted text.
That's what I'm thinking right now. I actually really really look forward to doing layout on a book. I used to be a designer before I was a programmer.
> maybe turn it into wiki style website, so that other people could chip in and you would moderate the submitted text.
Sometime soon, I'll probably try to get the source repo public and up on github. Then I can take pull requests and let people file issues to fix bugs/errors in the book.
I have to do some sanitizing of the repo first, and figure out a license, but it's on my TODO list.
So, please consider finding a way to make this project worth your while as I really want to see this completed.
Do you run the pathfinding and set the position in each game tick, or does the pathfinding has it's own tick.
To find the path often takes longer than one update tick. The old way to handle distributing the job over multiple ticks was threads; the new way is coroutines.
If you don't care if everyone's opponents are equally smart, you can always glance at the clock each time through the loop/level of recursion and cut off the pathfinding if it's taking too long. If you're planning all of the routes through a map (at least on a macro scale - hierarchical, highway-like pathfinding saves a lot of time if you can stand the occasional quirks of its behavior) at runtime, you might be able to set up a system that steals a few cycles whenever there's time left over while the previous area is being played and then only blocks if it still isn't done when needed. But at that point, why not compute those paths really ahead of time (at map build) and then load them in as part of a map file?
But, one question: in the "Subclass Sandbox" (http://gameprogrammingpatterns.com/subclass-sandbox.html) chapter, shouldn't the activate member function be public? That is, isn't that function the interface to the outside world? As written, the Superpower abstract class has no external interface.
Here's my implementation of a component-based game object system, if interested:
Otherwise, quite informative.
+1 to this site for tackling this overused anti-pattern.