This, I think, one of the fundamental challenges of teaching software architecture or other higher-level engineering topics. A lot of architecture boils down to:
1. Here's an obvious simple way to solve a problem, X.
2. If you do X at scale, much later, you will run into pain.
3. So here's Y, which seems weird and non-obvious but solves the same problem as X without the pain.
It's really hard to teach people step 2 when they haven't experienced it first-hand. You can walk them through scenarios, but most scenarios small enough to fit in a reasonable amount of prose feel fake and contrived.
Even when this does work, you can end up with a generation of programmers that cargo cult Y without any real understanding of the original problems it avoids from X. Look at, for example, how "goto" is now a boogeyman in programming well beyond the narrow concrete problems Dijkstra had with it.
For many of the chapters of the book, I try to walk readers through the problems of simpler approaches as well as I can, but it's a continuing challenge. There really is no substitute for experience.
That being said, the point about doing X at scale wasn't my issue. Take for example programming language books and/or documentation. The typical approach in almost every book/guide is teaching you about syntax, data types, control structures, etc. While this is valuable information, it's not usually what a person new to the language is looking for. What they're looking for is (most probably) what is unique about this language in solving specific domains of problems. New to Erlang? You probably should be introduced to a problem that deals with concurrency and reliability, and get walked through an example of how Erlang can solve it elegantly. New to Python? You should probably be introduced to a problem that deals with manipulating data, and get walked through an example of how Python can help you wrangle data easily. Etc.
> For many of the chapters of the book, I try to walk readers through the problems of simpler approaches as well as I can, but it's a continuing challenge.
Please continue to do so. I'm sure the praise you get for your book is well deserved, and part of the reason is your approach to teaching the patterns.
> There really is no substitute for experience.
Agreed. And part of the teaching style I'm advocating for is making the learner "earn" the skill by showing them the way, not the destination.
What we didn't know was that there was a slab roller sitting next to us all along that the teacher hadn't mentioned during the start of the course.
Through natural progression of working on "advanced" projects, we were then introduced to the slab roller as it cut time to focus on the new techniques we were learning.
The teacher essentially did that for the remainder of the course and the ones that stayed behind to learn a bit more were able to use the shortcuts before the others because they were progressing on their off time.
For example, you can teach people programming with higher level languages. But if your goal is to fully understand programming (as a professional programmer), you can start with the oldest discoveries and work your way into more recent years. That way you are able to grasp the full scope, know benefits and drawbacks, etc. You are also less likely to make the same mistakes of the past.
The one that springs to mind for me is Functional Programming in Scala.
It's essentially taking a coaching mentality towards your writing, letting the reader take the steps themselves to feel confident in it, and then only afterwards telling them what they are actually doing (e.g. in that context, properly serving).
It's a fantastic process and the book, of course, explains how it works in the context of teaching tennis.
Back then I was doing mobile game development for PocketPC.
After I left that company, some ex-colleague had to do some changes in my code, or port it, I can't remember. So he was looking at that game loop that I implemented, and was thinking "What the hell is he doing here???". So he did a google search to see if he could find something, and immediately stumbled upon an article, written by that same programmer :D.
He told me years later when I saw him, and said "What are the chances that you search something on google because of code you're going through, and find the article that explains it written by the same programmer".
I was still a junior back then, so probably I should have commented my code a bit better (instead of not at all ;)).
I still want to write a revised deWiTTERS game loop article, because the steps in a physics engine could make the extrapolation redundant.
As an example, if we want to move something on screen at a constant rate in pixels per second, the number of pixels to move per update (ie frame) at 60 FPS is half as far as when updating at 30 FPS. The result is that the position at any time is the same regardless of how quickly the computer ran the simulation.
(curr_frame_tick - prev_frame_tick) is the delta time of each loop. So this value is the time it took to do the previous loop.
It doesn't increase each timestep because prev_game_tick is recalculated each step.
Step1 (initial step):
- prev_frame_tick = 0
- curr_frame_tick = 0
- delta = 0
- curr_frame_tick = 200
- delta = 200
- prev_frame_tick = 200
- curr_frame_tick = 356
- delta = 156
next_world_state = UpdateWorld(inputs, previous_world_state)
Taking that concept to an operating system essentially.
> Your urbit is a deterministic computer in the sense that its state is a pure function of its event history. Every event in this history is a transaction; your urbit's state is effectively an ACID database.
> Because each urbit is deterministic we can describe its role appropriately in purely functional terms: it maps an input event and the old urbit state to a list of output actions and the subsequent state. This is the Urbit transition function.
Demonstration and explanation here: https://www.youtube.com/watch?v=P13KmJBNn1c
OpenArena added an option for fixed framerate physics, and an option to disable the rounding.
(Somewhere in the Second Life viewers, there's a lock conflict which causes disk delays in loading assets from the local cache to stall input processing in the main loop. All the disk I/O is in other threads, and those threads can talk directly to the vertex buffer memory in the graphics card, so this shouldn't be happening. But it is.)
I find it funny how the thought goes to deep space, yet reaches so close to Earth.
Best book to learn programming patterns in general if you ask me
I wish there were other Pattern and Software-Engineering books that discussed things in more domain-specific contexts, but I suspect game development is one of the few domains large enough, and with enough people looking for domain specific literature, to really warrant such a book.
Maybe it's just me, but if I'm going to read a few thousand words about some piece of software architecture, I'd rather be reading about trolls and magic than employee record databases.
I've come across very few Design Patterns, and Software Engineering topics in general, that couldn't be taught/explained from the perspective of game development. I'm surprised that it isn't used as a teaching-tool more often then it is. It's an inherently complex domain that most audiences are already quite familiar with; the "why" behind things is either immediately obvious or easily explained. If someone doesn't know what the requirements are for something like an employee record databases, and how such a system might be used, using that as a context for teaching is not much more useful then "class Widget" and "Foo.Bar()".
The only time these overlap is in video games, which is usually not used as the context for teaching software development when OOP is taught.
I'll often look at stuff and wonder: what is your purpose? In my experience, explanations without context aren't generally very useful. It's much easier to incorporate knowledge when you're first presented with a concrete example of a problem along with its solution, and then you're presented with the abstraction.
One of the most notorious examples I can think of is when someone tries to explain monads. For some reason people often start by providing a long mathematical definition, which is completely useless if you don't even understand the weird words being used. It's worth learning Haskell just for all the new concepts and ideas it'll expose you to, even though the syntax looks very unfamiliar at first.
Which version pays you the most?
First of all, thank you for caring about this! Since I self-published, I set the prices so that the royalties are about the same for each format. (I also get the lion’s share of the money since there’s no publisher taking a cut.)
Buy the format you want and I’ll get paid pretty much the same either way. If you want to give me money, but don’t actually want a physical book, consider giving it to a friend or your local library. I get money, you feel good, and someone gets a free book!
For example a falling object will clip in the ground for tens of ms of displacement which is definitely visually significant.
A better solution is to either only interpolate and live with the additional frames of input latency (fine for a lot of games) or resimulate the next physics frame with the new player input, and live with the additional CPU overhead.
I’ve had a great experience so far following Glenn’s suggested solution.
This article presents the most cogent and approachable explanation of the topic I've seen. It's empathetic style reminds me of the excellent "Head First" design patterns book (which if your ego can get past the initially-jarring style, is IMO the best way to fully grok the patterns). Thanks to the author, to to the person who shared it!
A rendered frame is just a view on some data. As soon as you couple state updating to rendering you're bound to encounter issues. This is what leads to games like ARMA who's FPS in multiplayer depends heavily on the performance server, which is insane.
Not necessarily. For games one of the things you're trying to minimize is time from user input to response on screen - that has to go through your input system, your animation system, possibly your physics system, and then all the way through the renderer. You need strict control of ordering and buffering. To allow stuff to be offloaded to another thread adds latency and can make things messy to control.
For simpler games, threads here don't help. They just hurt. Extra complexity, and likely extra latency (if only through mistakes and synchronization).
For more complex games, you can often get away with just going "wide" within the game loop. You have threads, but it's not about splitting off "animation" and "physics" jobs, but instead going wide when updating animation or updating physics. It's more granular and the high level coordination still happens in a traditional game loop.
If you want to get really fancy, your game loop starts updating a graph of systems of system jobs instead of a hardcoded list of system updates. If you strictly specify all inputs, modifications, outputs, etc. you can start making it automatically parallel by walking the graph and dispatching jobs that don't conflict with running jobs.
Even then you still have a game loop, you've just offloaded most of it's work to a fancy job system. Not all - you'll probably still have some API calls that only work on the main thread hardcoded into the game loop (e.g. for pumping OS UI messages using system provided APIs that aren't thread safe).