delta_t = time between last frame and now
fetch input status
for each entity: entity.think(delta_t) (for example, if a monster is in the state of moving towards me, it will move speed*delta_t units)
Render()
Never saw loops with real threads, that looks like asking for trouble.
If it was a multiplayer game then the think part was on the server and the client would just ready entity state from the network, sent input to server, and render().
entities.forEach { $0.think(delta_t) }
is essentially cooperative multitasking, whose efficiency is questionable in general and especially on multicore hardware - but I don't know about games. Let alone programming think(delta_t) adds complexity and introduces more states within each entity, compared to real threads with their own stacks.There is no silver bullet. There are decades of academic research and engineering on this and we have yet to reach a good answer.
Typically though the updating the game itself is a relatively small part of the frame time and can be reasonably tricky to get real gains through parallelisation.
Copying and passing immutable data around is not always fast enough and definitely not trivial to merge the results back.
In summary, it is a hard problem with no general solution. If you could solve it, then you have solved multithreading design for any problem.
Having a system thread for every entity in a game doesn’t scale. Also having to synchronise the threads of entities by hand would be quite a nightmare.
It scales a lot better to have a thread pool, with a thread for each core, and then running lightweight jobs on them. So pretty much the same idea like e.g. the goroutines in Go or the green threads of Erlang.
Having such a job system you also have a lot less hassle with thread synchronisations. I’m working on an interactive 3d application and the job system we’ve have works like a charm for parallelization.
The benefit of threads - green or not - is also that you can maintain the state in the stack and benefit from functional style programming, which is generally more robust and easier to write compared to state machines.
Parallelism has many forms and from my experience you can always find ways of minimizing synchronization in particular tasks. For example, you can sometimes replace a whole queue with a single atomic pointer when you know messages override previous ones semantically, i.e. the consumer is only ever interested in the most recent one.
When multicore is utilized it's normally in the line of https://unity.com/dots
Some assumptions that may be terribly wrong:
- The user input loop (keyboard, mouse) is still the main loop somewhere at the very bottom. Or is it different in RPGs?
- Spawn threads each with their own inner loop, for each complex agent. Not necessarily an OS thread, but something that has its main loop and a stack.
- Objects that don't have a "brain" don't need a thread, e.g. the ball in the tennis game. All such objects are managed from a single thread.
- There's a rendering loop, possibly with its own complex multithreaded architecture.
I don't know, did I come close to what's actually happening in complex RPG games?