Long time ago I messed around with multi-threading and Box2D. When I researched into the matter, people in forums everywhere said that threading and Box2D is a terrible idea.
I thought, screw everyone, I’ll make it happen, and jumped right into making a physics objects producer.
Many months and a few thousand lines of code later I have to admit: It kind of is.
The Issue with Multi-Threading
The first Box2D multi-threading setup I designed was terrible and buggy. I won’t get into detail about this system – let me just say the world.step() method was outsourced in its own physics thread, required a very tedious thread-“piping” system and on top of all, crashed frequently due to world physics access during time steps and other synchronization issues. This is based on a client-server pattern, where the main thread is the “client”, and the physics thread the “server”.
It was a pretty bad solution causing an unplayable game. Go look: CamoTactics 7.2 will crash within 3-5 minutes of gameplay. I thought to myself:
“How can it be that a buggy physics system ruins my game? This needs to change!”
So I completely overworked this old design with a completely new approach. This method works much better.
Some Introductory Thoughts
In order to achieve multi-threading, I somehow must manage communications between threads, since one thread cannot easily access common resources with another thread without causing access violations and synchronization issues. The basic idea is that I must somehow have an input and an output in order to communicate.
I also need to know what request created which object. I need to know when the object I requested finished and proceed with whatever I was doing after I obtained it. That also means, I must wait until it was produced. To do this, each incoming request gets an ID, and when the object finished producing, it is removed from the requests, and placed with the same ID into the output. Ever went to government institutions and were asked to draw a number and wait until you’re called? That’s the very same process here. The class ConcurrentHashMap is very handy for this task.
How it works
Instead of making the physics simulation (= world.step()) run in its own thread, I kept it in the main thread (how it’s supposed to be). I encapsulated that in a factory that solely produces objects, and a service that handles synchronization. These two things together act as producer.
The strict separation of producing/syncing function may seem overkill at first but my code is as clean as it can get. I didn’t separate at all in the last design and it was a messy monster.
Both factory and service produce or obtain low-level objects. Such are for example, Box2D objects like bodies, fixtures, joints and callbacks (area queries or ray cast). That’s all you need. The body factory looks very simple:
The service, too, is relatively simple. Note the synchronized keyword and queue/obtain methods. BodyDef, JointDefs etc. objects are used to queue and stored internally for later production. BodyFixtureDef is a helper object that groups a BodyDef together with one or more FixtureDefs.
The production method looks like this and is invoked in the main thread:
If you’re upset I just posted pics instead of copy-paste-able code: I plan to provide the entire code base as example once I’m done refactoring it 🙂
Now I just need a “client”, here called “worker”, that runs in its own thread asking the main thread to produce objects via the service.
I went a bit further and made one generic worker class capable of executing any task. Each task has a execute() function that is being called by the worker thread. To make implementation easier, I assume that each task has no return type. Using these tasks the worker assembles high-level objects in a shape or form your game needs.
For example, the SpawnEntity task for CamoTactics composes of the following:
- attach fixtures to one body
- assemble multiple bodies to a joint
- making proper collision filtering adjustments
- combining physics with actors or entities (the data side of your game)
- place the finished entity in a registry
As you can see, there is nothing returned here because the SpawnEntity class is in itself final and complete.
Examples of tasks: SpawnEntity, CreateTerrainColliders, CreateMapItems, or any other physics related task your game needs to accomplish.
Here’s the System in Action
The worker is capable of creating physics objects in real time without lag, e.g. used for creating a map and its objects. Here collision for rock terrain is created.
In a very specialized form of worker (PermaWorker) that runs permanently checking for incoming tasks (such as spawning projectiles) I managed to get 300 bullets on screen that are quickly created and also destroyed. Note how there is no lag or stuttering.
There are other applications for this worker pattern, for example loading game with a loading screen!
But that’s for another article. 😉
- Box2D Tutorials at iforce2d.net
- wizardfu Box2D Tutorials
- Graphs made with draw.io
- What formats to store data in?