Facebook Pixel
Searching...
English
EnglishEnglish
EspañolSpanish
简体中文Chinese
FrançaisFrench
DeutschGerman
日本語Japanese
PortuguêsPortuguese
ItalianoItalian
한국어Korean
РусскийRussian
NederlandsDutch
العربيةArabic
PolskiPolish
हिन्दीHindi
Tiếng ViệtVietnamese
SvenskaSwedish
ΕλληνικάGreek
TürkçeTurkish
ไทยThai
ČeštinaCzech
RomânăRomanian
MagyarHungarian
УкраїнськаUkrainian
Bahasa IndonesiaIndonesian
DanskDanish
SuomiFinnish
БългарскиBulgarian
עבריתHebrew
NorskNorwegian
HrvatskiCroatian
CatalàCatalan
SlovenčinaSlovak
LietuviųLithuanian
SlovenščinaSlovenian
СрпскиSerbian
EestiEstonian
LatviešuLatvian
فارسیPersian
മലയാളംMalayalam
தமிழ்Tamil
اردوUrdu
Game Programming Patterns

Game Programming Patterns

by Robert Nystrom 2011 354 pages
4.48
1k+ ratings
Listen
Listen

Key Takeaways

1. Software architecture is about managing change and minimizing cognitive load.

For me, good design means that when I make a change, it’s as if the entire program was crafted in anticipation of it.

Architecture's primary goal. Software architecture is fundamentally about facilitating change. A well-designed system anticipates future modifications, allowing developers to implement new features or fix bugs with minimal disruption. The ease with which a codebase accommodates changes is the ultimate measure of its architectural quality.

Cognitive load reduction. A key aspect of good architecture is minimizing the amount of information a developer needs to understand before making a change. Decoupling patterns and modular design help reduce this cognitive load by allowing developers to reason about individual components in isolation. This minimizes the risk of unintended side effects and makes the codebase easier to navigate.

The programming flow. The programming flow involves understanding existing code, devising a solution, implementing the code, and reorganizing the code. Software architecture is about the learning phase. Loading code into neurons is slow, so it pays to find strategies to reduce the volume of it.

2. Decoupling reduces the volume of code needed to understand a change.

If you de-couple them, you can reason about either side independently.

Decoupling defined. Decoupling refers to the degree to which two pieces of code can be understood and modified independently. Tightly coupled code requires a developer to understand the intricacies of both components before making any changes, increasing the risk of errors and making maintenance more difficult. Decoupling aims to minimize these dependencies, allowing developers to focus on individual components without needing to grasp the entire system.

Benefits of decoupling. Decoupling minimizes the amount of knowledge you need to have before you can make progress. Decoupling also means that a change to one piece of code doesn’t necessitate a change to another. The less coupling we have, the less that change ripples throughout the rest of the game.

Decoupling strategies. Decoupling can be achieved through various design patterns and architectural principles, including interfaces, abstract classes, and message queues. These techniques allow components to interact with each other through well-defined contracts, reducing the need for direct dependencies and promoting modularity.

3. Abstraction and extensibility come at the cost of complexity and speculation.

Whenever you add a layer of abstraction or a place where extensibility is supported, you’re speculating that you will need that flexibility later.

The allure of abstraction. Abstraction, modularity, and design patterns can lead to well-architected programs that are a joy to work in. Good architecture makes a huge difference in productivity. However, these benefits come at a cost.

The cost of flexibility. Good architecture takes real effort and discipline. 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.

The YAGNI principle. Overzealous abstraction can lead to codebases with excessive interfaces, plugin systems, and virtual methods, making it difficult to trace the actual code that performs a task. The "You Aren't Gonna Need It" (YAGNI) principle serves as a reminder to avoid premature optimization and focus on solving the immediate problem at hand.

4. Performance optimization relies on assumptions and concrete limitations.

Performance is all about assumptions.

Flexibility vs. Performance. 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. But performance is all about assumptions. The practice of optimization thrives on concrete limitations.

Optimization trade-offs. 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.

The prototyping compromise. 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. It’s easier to make a fun game fast than it is to make a fast game fun.

5. Simplicity eases constraints on architecture, performance, and development speed.

I try very hard to write the cleanest, most direct solution to the problem.

Simplicity as a guiding principle. Writing clean, direct solutions to problems minimizes the amount of code, which in turn reduces the cognitive load required to understand and modify it. Simple code often runs faster due to reduced overhead and fewer lines of execution.

Simplicity and time. Simple code doesn't necessarily take less time to write. A good solution isn’t an accretion of code, it’s a distillation of it. 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.

The value of elegance. Elegant solutions are general ones: 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.

6. The Command pattern reifies method calls for configurable input and undo/redo.

A command is a reified method call.

Reified method calls. The Command pattern encapsulates a request as an object, allowing for parameterization of clients with different requests, queuing, logging, and support for undoable operations. It is a method call wrapped in an object.

Input configuration. The Command pattern enables user-configurable input mappings by associating each button press with a Command object. This allows players to customize their controls without requiring changes to the core game logic.

Undo/redo functionality. By implementing execute() and undo() methods in Command subclasses, the Command pattern simplifies the implementation of undo/redo functionality. Each command stores the necessary state to reverse its effects, enabling users to easily revert actions.

7. The Flyweight pattern conserves memory by sharing intrinsic state.

Flyweight, like its name implies, comes into play when you have objects that need to be more lightweight, generally because you have too many of them.

Lightweight objects. The Flyweight pattern reduces memory consumption by separating an object's state into intrinsic (shared) and extrinsic (unique) components. The intrinsic state is stored in a shared object, while the extrinsic state is passed in as needed.

Terrain example. The Flyweight pattern can be applied to terrain tiles in a game world. Each tile can store a pointer to a shared Terrain object, which contains the terrain type's properties (e.g., movement cost, texture). This avoids storing redundant data for each tile.

Hardware support. The Flyweight pattern may be the only Gang of Four design pattern to have actual hardware support. With instanced rendering, the graphics card can render the shared data just once. Then, separately, it pushes over every tree instance’s unique data — its position, color, and scale.

8. The Observer pattern enables decoupled notifications, but requires careful management.

It lets one piece of code announce that something interesting happened without actually caring who receives the notification.

Decoupled communication. The Observer pattern allows one object (the subject) to notify multiple other objects (observers) about state changes without knowing their specific types. This promotes loose coupling and modularity.

Achievement system example. The Observer pattern can be used to implement an achievement system. The physics engine can notify observers when an entity falls, and the achievement system can check if the entity is the hero and if it fell off a bridge to unlock the "Fall off a Bridge" achievement.

Memory management. When using the Observer pattern, it's important to manage the lifetime of subjects and observers to avoid dangling pointers or memory leaks. Observers should unregister themselves from subjects when they are destroyed.

9. The Prototype pattern offers cloning as an alternative to traditional instantiation.

The key idea is that an object can spawn other objects similar to itself.

Prototypal object creation. The Prototype pattern allows new objects to be created by cloning existing objects (prototypes). This avoids the need for complex constructor logic and enables the creation of objects with customized initial states.

Spawner example. The Prototype pattern can be used to implement monster spawners. Instead of having a separate spawner class for each monster type, a single spawner class can clone a prototype monster instance to create new monsters.

Data modeling. Prototypal delegation is a good fit for defining data in games. The magic Sword of Head-Detaching, which is really just a longsword with some bonuses, can be expressed as that directly:

{
"name": "Sword of Head-Detaching",
"prototype": "longsword",
"damageBonus": "20"
}

10. The State pattern encapsulates state-specific behavior, but can be overused.

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.

State-dependent behavior. The State pattern allows an object to alter its behavior based on its internal state. Each state is represented by a separate class, and the object delegates method calls to its current state object.

Heroine example. The State pattern can be used to implement the behavior of a heroine in a platformer game. The heroine can have states such as standing, jumping, ducking, and diving. Each state class defines the heroine's behavior for each input.

State transitions. To change states, we need to assign state_ to point to the new one. If the state object doesn’t have any other fields, then the only data it stores is a pointer to the internal virtual method table so that its methods can be called. In that case, there’s no reason to ever have more than one instance of it.

11. The Game Loop pattern decouples game time from user input and processor speed.

Decouple the progression of game time from user input and processor speed.

Consistent gameplay speed. The Game Loop pattern ensures that the game runs at a consistent speed regardless of the underlying hardware or user input. This is achieved by decoupling the game's update logic from the rendering process.

Variable time steps. In a variable time step game loop, the amount of time that passes between each update is not fixed. Instead, it varies depending on the frame rate. The engine is then responsible for advancing the game world forward by that amount of time.

Fixed update time step, variable rendering. The game simulates at a constant rate using safe fixed time steps across a range of hardware. It’s just that the player’s visible window into the game gets choppier on a slower machine.

12. The Update Method pattern simulates independent objects through sequential updates.

Simulate a collection of independent objects by telling each to process one frame of behavior at a time.

Concurrent object simulation. The Update Method pattern simulates a collection of independent objects by calling an update() method on each object every frame. This gives each object a chance to update its state and behavior.

Entity example. The Update Method pattern can be used to simulate entities in a game world. Each entity has an update() method that is called every frame. The update() method can handle things like AI, physics, and animation.

Performance considerations. The Update Method pattern can be optimized by using data locality techniques. This involves storing the data for all entities in a contiguous block of memory, which can improve cache performance.

13. Double Buffering creates the illusion of simultaneity by decoupling data access from modification.

Cause a series of sequential operations to appear instantaneous or simultaneous.

Atomic state updates. The Double Buffer pattern allows for incremental modification of state while ensuring that external code always sees a consistent, atomic snapshot of the data. This is achieved by maintaining two copies of the data: a current buffer and a next buffer.

Graphics rendering example. The Double Buffer pattern is commonly used in graphics rendering to prevent tearing. The rendering code writes to the next buffer, while the video driver reads from the current buffer. When the rendering is complete, the two buffers are swapped.

Performance considerations. The swap itself takes time. Double-buffering requires a swap step once the state is done being modified. That operation must be atomic — no code can access either state while they are being swapped.

14. The Service Locator pattern provides a global access point to services while minimizing coupling.

Provide a global point of access to a service without coupling users to the concrete class that implements it.

Decoupled service access. The Service Locator pattern provides a global point of access to a service without coupling the code that uses the service to the concrete implementation. This allows for greater flexibility and testability.

Audio system example. The Service Locator pattern can be used to provide access to the audio system. The Locator class provides a getAudio() method that returns an instance of the Audio interface. The actual implementation of the audio system can be swapped out without affecting the code that uses it.

Null service. If the service can’t be located, the locator can return a null service. The null service implements the service interface, but doesn’t actually do anything. This allows the game to continue running even if the service is not available.

15. The Subclass Sandbox pattern defines behavior in subclasses using base class operations.

Define behavior in a subclass using a set of operations provided by its base class.

Constrained subclass behavior. The Subclass Sandbox pattern defines behavior in a subclass using a set of operations provided by its base class. This limits the subclass's access to the rest of the system, promoting encapsulation and reducing coupling.

Superpower example. The Subclass Sandbox pattern can be used to implement superpowers in a game. The Superpower base class provides methods for moving the hero, playing sounds, and spawning particles. Subclasses can then implement specific superpowers by calling these methods.

Wide inheritance trees. This pattern leads to an architecture where you have a shallow but wide class hierarchy. Your inheritance chains aren’t deep, but there are a lot of classes that hang off Superpower. By having a single class with a lot of direct subclasses, we have a point of leverage in our codebase.

16. The Bytecode pattern gives behavior the flexibility of data through a virtual machine.

Give behavior the flexibility of data by encoding it as instructions for a virtual machine.

Data-driven behavior. The Bytecode pattern allows behavior to be defined in data rather than code. This enables greater flexibility, easier modification, and safer sandboxing.

Spell system example. The Bytecode pattern can be used to implement a spell system in a game. Each spell is defined as a sequence of bytecode instructions that are executed by a virtual machine. This allows designers to create new spells without having to write code.

Stack machine. The VM maintains an internal stack of values. In our example, the only kinds of values our instructions work with are numbers, so we can use a simple array of ints. Whenever a bit of data needs to work its way from one instruction to another, it gets there through the stack.

Last updated:

Review Summary

4.48 out of 5
Average of 1k+ ratings from Goodreads and Amazon.

Game Programming Patterns is highly praised for its clear explanations, engaging writing style, and practical examples of design patterns applied to game development. Readers appreciate the author's humor, insights into performance considerations, and balanced discussion of pros and cons for each pattern. Many found it valuable for both beginners and experienced developers, with some noting its relevance beyond game programming. A few criticisms mention outdated C++ examples and a focus on certain game types. Overall, most reviewers consider it an essential read for game developers.

Your rating:

About the Author

Robert Nystrom is a seasoned programmer with two decades of professional experience, including eight years at Electronic Arts. He has worked on various game projects across multiple platforms, from large franchises like Madden to smaller titles. Nystrom is particularly proud of his contributions to tools and shared libraries that empower other developers. His passion lies in creating clean, usable code that enhances the creative capabilities of others. Based in Seattle with his family, Nystrom is known for his hospitality and culinary skills. His book on game programming patterns reflects his extensive experience and desire to share knowledge with fellow developers.

Download PDF

To save this Game Programming Patterns summary for later, download the free PDF. You can print it out, or read offline at your convenience.
Download PDF
File size: 0.29 MB     Pages: 16

Download EPUB

To read this Game Programming Patterns summary on your e-reader device or app, download the free EPUB. The .epub digital book format is ideal for reading ebooks on phones, tablets, and e-readers.
Download EPUB
File size: 2.97 MB     Pages: 16
0:00
-0:00
1x
Dan
Andrew
Michelle
Lauren
Select Speed
1.0×
+
200 words per minute
Create a free account to unlock:
Requests: Request new book summaries
Bookmarks: Save your favorite books
History: Revisit books later
Ratings: Rate books & see your ratings
Try Full Access for 7 Days
Listen, bookmark, and more
Compare Features Free Pro
📖 Read Summaries
All summaries are free to read in 40 languages
🎧 Listen to Summaries
Listen to unlimited summaries in 40 languages
❤️ Unlimited Bookmarks
Free users are limited to 10
📜 Unlimited History
Free users are limited to 10
Risk-Free Timeline
Today: Get Instant Access
Listen to full summaries of 73,530 books. That's 12,000+ hours of audio!
Day 4: Trial Reminder
We'll send you a notification that your trial is ending soon.
Day 7: Your subscription begins
You'll be charged on Mar 1,
cancel anytime before.
Consume 2.8x More Books
2.8x more books Listening Reading
Our users love us
50,000+ readers
"...I can 10x the number of books I can read..."
"...exceptionally accurate, engaging, and beautifully presented..."
"...better than any amazon review when I'm making a book-buying decision..."
Save 62%
Yearly
$119.88 $44.99/year
$3.75/mo
Monthly
$9.99/mo
Try Free & Unlock
7 days free, then $44.99/year. Cancel anytime.
Settings
Appearance
Black Friday Sale 🎉
$20 off Lifetime Access
$79.99 $59.99
Upgrade Now →