Box2D JSON loader - b2dJson


Intro


It is often useful to be able to save the state of the Box2D world and reproduce it later, whether for loading a game level, a saved game, tuning the parameters of your simulation, or just simply for debugging. A common method for doing this is to loop through all the bodies, fixtures and joints in the world and write them to a text file.

XML is often the first choice of file format since it's easily loadable in many different languages. Likewise, JSON (javascript object notation) which is in many ways the successor to XML is also handy choice. On this page you will find details on 'b2dJson', a text file dump/load utility for Box2D worlds written in C++ and using JSON for formatting.

Update (Nov 2 2012) The full specification of the JSON format used by b2dJson can be found here: JSON structure

Update (Nov 6 2012) A Javascript version of b2dJson is now available, based on box2dweb. Click here for a demo: Javascript vehicles demo, and here to download a simple example which you can run straight from your hard drive (no webserver needed): b2dJson-box2dweb-testbed.zip. Note that this version only loads scenes from JSON, saving scenes has not been implemented.

Update (Nov 11 2012) A Java version of b2dJson is now available, based on JBox2D. Usage has been kept as close as possible to the C++ version. Look for the "Click here for Java version" links below for details.

Update (Jan 10 2013) Source code has been updated to support custom properties. See the 'Custom properties' section below for details.

Update (Mar 9 2013) Source code is now managed at github. See the 'Source code' section below for details.



Features


The b2djson utility has two main functions. Firstly, it can take a b2World* pointer and convert it to a textual representation. This is essentially just one big string which you can pass around in your program as a string, or write to a file. The second function is the reverse of this - to take one of these strings and create a Box2D b2World from it. Along the way, there are other handy features which are also possible:
  • attach names to objects of interest in the world (joint, body, fixture)
  • add arbitrary custom properties to objects (body, fixture, joint, world)
  • replicate individual parts of the world (body, fixture)
Attaching a name to something in the world when you save the textual representation lets you get a reference to that object when you load the world again. Replicating parts of the world could be thought of as a rudimentary copy+paste feature.

b2dJson uses the lightweight jsoncpp to handle the actual JSON reading and writing in a very stress-free way - many thanks to the jsoncpp devs!
For Java, the org.json implementation of JSON is used.



Basic usage


The b2djson utility is typically used by declaring an instance in local scope and calling functions on this instance. To write a Box2D world to a file, you would do this:
1
2
  b2dJson json;
  json.writeToFile(myWorld, "myfile.json");
1
2
3
4
  Jb2dJson json = new Jb2dJson();
  StringBuilder errorMsg = new StringBuilder();
  if ( ! json.writeToFile(myWorld, "myfile.json", 4, errorMsg) )//4-space indent
      System.out.println(errorMsg);
And to load the file you created, you would do this:
1
2
3
  string errorMsg;
  b2dJson json;
  b2World* myWorld = json.readFromFile("myfile.json", errorMsg);
1
2
3
  Jb2dJson json = new Jb2dJson();
  StringBuilder errorMsg = new StringBuilder();
  World world = json.readFromFile("snapshot.json", errorMsg);
To create a string with the same information that would have been written to the file, you can do this instead:
1
2
3
4
5
6
7
8
  b2dJson json;
  string mystring = json.writeToString(myWorld);
  
  //later...
  
  string errorMsg;
  b2dJson json;
  b2World* myWorld = json.readFromString(mystring, errorMsg);
1
2
3
4
5
6
7
8
  Jb2dJson json = new Jb2dJson();
  String mystring = json.worldToString(myWorld);
  
  //later...
  
  Jb2dJson json = new Jb2dJson();
  StringBuilder errorMsg = new StringBuilder();
  World myWorld = json.readFromString(mystring);
If the recreation of the world from the JSON data fails, each of the 'readFrom' functions will return null. You can check the value of the 'errorMsg' variable to see what happened.



Retrieving objects of interest


If you want to attach names to certain objects of interest in the world, you will need to do that before you create the textual representation. You can attach names to joints, bodies and fixtures. Each object can have only one name. Multiple objects can have the same name. Here is an example:
1
2
3
4
5
6
7
8
9
10
11
12
13
  b2Joint* axleJoint;
  b2Body* carBody;
  b2Fixture* rearBumperFixture;
  b2Fixture* frontBumperFixture;
  
  b2dJson json;
  
  json.setJointName(axleJoint, "drive axle");
  json.setBodyName(carBody, "chassis");
  json.setFixtureName(rearBumperFixture, "bumper");
  json.setFixtureName(frontBumperFixture, "bumper");
  
  json.writeToFile(myWorld, "myfile.json");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  Joint axleJoint;
  Body carBody;
  Fixture rearBumperFixture;
  Fixture frontBumperFixture;
  
  Jb2dJson json = new Jb2dJson();
  
  json.setJointName(axleJoint, "drive axle");
  json.setBodyName(carBody, "chassis");
  json.setFixtureName(rearBumperFixture, "bumper");
  json.setFixtureName(frontBumperFixture, "bumper");
  
  StringBuilder errorMsg = new StringBuilder();
  json.writeToFile(myWorld, "myfile.json", 4, errorMsg);//4-space indent
Notice that we have given the same name to both bumpers. For this example, after you load the world from the JSON data you can retrieve the named objects like this:
1
2
3
4
5
6
7
8
9
10
  vector<b2Joint*> axleJoints;
  vector<b2Body*> carBodies;
  vector<b2Fixture*> bumperFixtures;
    
  b2dJson json;
  b2World* myWorld = json.readFromFile("myfile.json");
  
  json.getJointsByName("drive axle", axleJoints);
  json.getBodiesByName("chassis", carBodies);
  json.getFixturesByName("bumper", bumperFixtures);
1
2
3
4
5
6
7
  Jb2dJson json = new Jb2dJson();
  StringBuilder errorMsg = new StringBuilder();
  World myWorld = json.readFromFile("myfile.json", errorMsg);
  
  Joint[] axleJoints = json.getJointsByName("drive axle");
  Body[] carBodies = json.getBodiesByName("chassis");
  Fixture[] bumperFixtures = json.getFixturesByName("bumper");
In this example, this would result in two fixtures for the bumpers, because they had the same name.



Merging files into the same world


(Added Nov 3 2013, currently for C++ version only) Please use the latest source from github!

Sometimes it's useful to keep things separated in different files, and then load them into the same world. For example you might want to keep your game levels in one file, and load in the player and enemies from other files. You can do this by giving an existing world as a parameter to the readFromFile function, like this:
1
2
3
4
5
6
7
  string errorMsg;
  
  b2dJson json1;
  b2World* world = json1.readFromFile( "level1.json", errorMsg );
  
  b2dJson json2;
  json2.readFromFile( "bike.json", errorMsg, world );
Notice that the second time, we have given the existing world as the final parameter. Keep in mind that the information for object names is still held separately in the b2dJson objects, so you must use the correct one to find any objects of interest, for example:
1
2
  b2Fixture* finishLine = json1.getFixtureByName( "finish" );  // use json1 (level)
  b2Fixture* bikeFrame = json2.getFixtureByName( "frame" );    // use json2 (bike)
Most likely, you will also want to move things into place after loading. This can be done by moving all bodies by the same distance. For example to move the bike that was loaded by json2 above, we could do:
1
2
3
4
5
6
7
8
9
  b2Vec2 delta( 1.2, 3.4 ); // move all bodies by this offset
  
  vector<b2Body*> bikeBodies;
  json2.getAllBodies(bikeBodies);
  
  for (int i = 0; i < bikeBodies.size(); i++) {
      b2Body* body = bikeBodies[i];
      body->SetTransform( body->GetPosition() + delta, body->GetAngle() );
  }



Using custom properties


You can add your own named properties to the main items: body, fixture, joint, world. The property types can be int, float, string, b2Vec2 or bool. Here is an example of setting each of these types in a body:
1
2
3
4
5
6
7
8
9
  b2dJson json;
  
  json.setCustomInt(myBody, "region", 5);
  json.setCustomFloat(myBody, "damage", 2.5);
  json.setCustomString(myBody, "role", "hazard");
  json.setCustomVector(myBody, "offset", b2Vec2(2,5));
  json.setCustomBool(myBody, "respawn", true);
  
  json.writeToFile(myWorld, "myfile.json");
1
2
3
4
5
6
7
8
9
10
  Jb2dJson json = new Jb2dJson();
  
  json.setCustomInt(myBody, "region", 5);
  json.setCustomFloat(myBody, "damage", 2.5);
  json.setCustomString(myBody, "role", "hazard");
  json.setCustomVector(myBody, "offset", new Vec2(2,5));
  json.setCustomBool(myBody, "respawn", true);
  
  StringBuilder errorMsg = new StringBuilder();
  json.writeToFile(myWorld, "myfile.json", 4, errorMsg);//4-space indent
After loading a JSON file, You can retrieve a list of all items matching a given value with the getXXXByCustomYYY() functions. The XXX part will be one of Bodies, Fixtures, Joints. The YYY part will be one of Int, Float, String, Vector or Bool. For example, to get a list of all bodies in the scene with custom string property "category" matching "enemy", you would do:
1
2
  vector<b2Body*> enemies;
  json.getBodiesByCustomString("category", "enemy", enemies);
1
2
  Vector<Body> enemies = new Vector<Body>();
  json.getBodiesByCustomString("category", "enemy", enemies);
There are also single item versions of these functions, which return only the first item found which matches the condition. For these single item versions, the XXX part of the function is the singular form, eg. Body, Fixture, Joint. For example, to find a fixture in the scene with custom int property "damage" matching 5 you would do:
1
  b2Fixture* f = json.getFixtureByCustomInt("damage", 5);
1
  Fixture f = json.getFixtureByCustomInt("damage", 5);
You can check what custom properties exist in an item and get their values using the hasCustomXXX and getCustomXXX functions where the XXX is the type of property. These getCustomXXX functions take an optional (compulsory for Java) parameter to specify a default value to return if the property does not exist:
1
2
3
4
5
6
7
  bool hasRegion = json.hasCustomInt(myBody, "region");
  
  //returns zero if the property does not exist
  float damage = json.getCustomFloat(myBody, "damage");
  
  //returns 25 if the property does not exist
  float health = json.getCustomFloat(myBody, "health", 25);
1
2
3
4
  boolean hasRegion = json.hasCustomInt(myBody, "region");
  
  //returns 25 if the property does not exist
  float health = json.getCustomFloat(myBody, "health", 25);
If no default value is given and the property does not exist, the default value will be zero, b2Vec2(0,0), empty string, or false (whichever is applicable to the property type being requested).



Replicating objects using JSON


As well as saving and loading an entire world you can also make use of the 'piecewise' functions that b2dJson itself uses, to copy and replicate individual parts of the world. Specifically, you can copy bodies and fixtures. Here is an example of copying a body:
1
2
3
4
5
6
7
8
9
  b2Body* carBody;
  b2dJson json;
  Json::Value bodyValue = json.b2j( carBody );
  
  //later...
  
  b2World* myWorld;
  b2dJson json;
  b2Body* body = json.j2b2Body(myWorld, bodyValue);
1
2
3
4
5
6
7
8
9
  Body carBody;
  Jb2dJson json = new Jb2dJson();
  JSONObject bodyValue = json.b2j( carBody );
  
  //later...
  
  World myWorld;
  Jb2dJson json = new Jb2dJson();
  Body body = json.j2b2Body(myWorld, bodyValue);
You don't need to know what a Json::Value (or JSONObject for Java) is, suffice to say it contains the same information that would be dumped to the text file, so when you recreate a body like this all the body's attributes, and all its fixtures with their attributes will be replicated. Replicating a fixture is similar, except the last step requires a body to attach the fixture to:
1
2
3
4
5
6
7
8
9
  b2Fixture* bumperFixture;
  b2dJson json;
  Json::Value fixtureValue = json.b2j( bumperFixture );
  
  //later...
  
  b2Body* carBody;
  b2dJson json;
  b2Fixture* myFixture = json.j2b2Fixture(carBody, fixtureValue);
1
2
3
4
5
6
7
8
9
  Fixture bumperFixture;
  Jb2dJson json = new Jb2dJson();
  JSONObject fixtureValue = json.b2j( bumperFixture );
  
  //later...
  
  Body carBody;
  Jb2dJson json = new Jb2dJson();
  Fixture myFixture = json.j2b2Fixture(carBody, fixtureValue);
This will 'paste' the fixture onto another body.

Custom properties will also be duplicated by this method.



Other details


Although the files produced by this utility are text files and are somewhat human-readable and occasionally it can be handy to delve in there and cut bits out, the intention is not to edit these files manually.

By default, floating point values are written to JSON as their hex string representation to perfectly preserve the floating point value, as opposed to writing an ascii representation which can be rounded off and must be parsed back into a float when loading. If you do want to edit the files manually, you can set the b2dJson to use human readable values by passing true to the constructor like this:
1
2
  b2dJson json(true); //enable human-readable floats
  json.writeToFile(myWorld, "myfile.json");
Currently no consideration has been made for 64 bit... anythings.

Java version
The Java version does not support non-human-readable floats, so all floating point numbers will be their ascii representation.



Source code

You can find the source code for all versions of b2dJson here: http://github.com/iforce2d/b2dJson

C++ version
The source code will work for v2.3.0 of Box2D.
You can also find the supporting jsoncpp source at sourceforge separately.

Java version
The source code will work for v2.2.1 of JBox2D.
You can find the supporting implementation of JSON for Java at org.json.



Binary download


You can download a windows binary of the testbed including the b2dJson utility. This includes some neat test scenes using b2dJson to load JSON data, and you can use Ctrl+S and Ctrl+L to save and load a 'snapshot' of the current world in any of the regular tests, including your own. Just bear in mind that the filename 'snapshot.json' is hardcoded so if you want to save different scenes, you'll have to rename the file.

Each of the demos loads JSON data, and some of them do a little more, for example retrieving named objects or replicating existing bodies. See the source code of each test for comments on these methods.

Java version
You can download a compiled JBox2D testbed which loads the vehicles tests here: Jb2dJson-JBox2D-compiled.zip.

View YouTube video
box2d b2djson demo raycast box2d b2djson demo raycast box2d b2djson demo raycast
box2d b2djson demo raycast