Creating Levels in Box2D
Note: The parser in this tutorial has been updated by the curve parser here. This tutorial still explains InkScape and the “floors/ceilings” idea.
->
This tutorial is geared for creating levels for the Flash version of Box2D. With a little work, you can also use these on other versions of Box2D.
Things you will need
- The latest Box2D from the SVN Repository
- Flash CS3/CS4 with ActionScript 3.0
- InkScape
- FlashDevelop (optional)
Creating Edges in Box2D
The easiest way of creating levels in Box2D uses edge chains, which can be be declared like so…
var bodyDef: b2BodyDef = new b2BodyDef(); var chainDef: b2EdgeChainDef = new b2EdgeChainDef(); chainDef.friction = 0.5; chainDef.restitution = 0.0; chainDef.vertices.push(new b2Vec2(x1, y1)); //Must pass b2Vec2 for line coordinates chainDef.vertices.push(new b2Vec2(x2, y2)); chainDef.vertexCount = chainDef.vertices.length; //# of points = however many in definition world.GetGroundBody().CreateShape(chainDef); //Create the edgeShape
However, coding these levels by hand will take you much too long, especially if your level uses curves. An easy alternative to manually coding these levels is to use InkScape, a program that allows you to create vector art and saves it in svg format.
Creating Levels in InkScape
Open up an InkScape document and go to File -> Document Properties. From here you may set the canvas size and enable a grid to help you draw.
Edges and Collision Checking
You may notice that the Y values in on the ruler in your InkScape document start at the bottom and increase as you go up. When we convert the InkScape SVG file into Box2D coordinates, the parser (converting part of the code) will determine the side of the line that checks for collision based on how your Y coordinates are set up.
Box2D will only check collision on one side of your lines. Which side of the line is checked for collision is determined by the way you draw it in InkScape.
When your Y coordinates start at the bottom and increase as you go up, lines that go from left to right are floors, while lines that go right to left are ceilings.
Notice how the ball jumps through the line in the middle of the world. This happens when it approaches from the top because that line was drawn from right-to-left, making it a ceiling.
Importing the SVG and Parsing it
When you are done drawing your level, save it as an SVG file and open it back up in notepad. We’re going to copy everything from the the beginning of the tag to the end of the tag into a Flash AS3 variable.
var svg = //All content inside SVG tags goes here ;
Now that we have imported the SVG, we’re going to parse it. Make a function that looks like the one below in your class.
import Box2D.Dynamics.b2World; import Box2D.Common.Math.b2Vec2; import Box2D.Collision.Shapes.b2EdgeChainDef; import Box2D.Collision.Shapes.b2ShapeDef; import Box2D.Dynamics.b2BodyDef; import Box2D.Collision.Shapes.b2Shape; //RATIO is your physics scale of pixels to meters public static function parseThisSVG(svg: XML, world: b2World, RATIO:Number): void { var ns: Namespace = svg.namespace(""); var bodyDef: b2BodyDef = new b2BodyDef(); var chainDef: b2EdgeChainDef = new b2EdgeChainDef(); chainDef.friction = 0.5; chainDef.restitution = 0.0; for each (var path: XML in svg..ns::path) { var d: String = path.@d; // split string at spaces or commas: var regExp: RegExp = / |,/; var args: Array = d.split(regExp); if (args[args.length-1] == "") { args.pop(); // removes last element; } var i: int = 1; // skip first element, which is always "M". chainDef.isALoop = false; chainDef.vertices.length = 0; while (true) { if (i == args.length - 2) { chainDef.vertices.push(new b2Vec2(args[i] / RATIO, args[i+1] / RATIO )); break; } else if (args[i+2] == "z" || args[i+2] == "Z" ) { chainDef.isALoop = true; break; } else if (args[i+2] != "L") { throw new Error("Unsupported: The SVG Path contains an arc command or move-to command or a relative coordinate."); } chainDef.vertices.push(new b2Vec2(args[i] / RATIO , args[i+1] / RATIO )); i += 3; } chainDef.vertexCount = chainDef.vertices.length; world.GetGroundBody().CreateShape(chainDef); } }
When you call this function pass the SVG variable and your Box2D world variable to it and it will create all of the defined lines from your SVG.
That concludes the tutorial. If you need help, e-mail me or leave a comment here and I’ll see what I can do. Seeing as this is my first tutorial, constructive criticism is highly encouraged.
->
Credit for the parsing function goes to Shaktool off the Box2D forums. Another thanks to him for helping update the parser in this tutorial to the curve parser.
Related posts:



July 1st, 2009 at 4:19 AM
hi, it looks great, but i have trouble to do this myself.
After i found the needed b2edgechain classes, i ran in the following problem:
ArgumentError: Error #1063: Argument count mismatch on Box2D.Dynamics::b2World/Step(). Expected 2, got 3.
also:
“Things you will need
* The latest Box2D from the SVN Repository (help)”
this help links to inkscape..
July 1st, 2009 at 11:31 AM
That error you’re getting is because the current downloadable version of Box2D still uses the “Step()” function with two arguments. That or you upgraded to the latest version and you need to add in the extra argument to the Step() call.
By default, I believe the argument “Position Iterations” was 10 in the old version. Basically, the higher you set the 3rd argument, the more accurate the collisions will be with less chance of overlapping other objects, but at the expense of CPU power.
I’ve updated the link to the latest version of Box2D from the SVN repository, but you’ll have to get a working SVN client to download it. I managed to stumble through it by trial-and-error using the SmartSVN client.
July 2nd, 2009 at 3:51 AM
thanks a lot. i got it working
October 18th, 2009 at 11:38 AM
hey Quest, i just had another quick question with this tool.
i’m finding that my levels are importing upside down, any ideas?
October 18th, 2009 at 2:46 PM
By upside down, you mean flipped across the X axis?
There might be something with the Y coordinates being reversed in Inkscape. When I wrote the tutorials, Y equals 0 at the bottom of the page and Y equals max document height at the top of the page.
If it’s reversed in your Inkscape version (I’m using v.46), there should be an option to change it back around. I can’t seem to find that option, though.
If nothing else, you could try modifying the parser to flip the Y coordinates or flipping your levels in Inkscape. Hokey solutions, but they should work.
(If you modify the parser, might read this link. It explains how the coordinates are saved internally.)
October 18th, 2009 at 10:24 PM
yes, these are flipped across the x-axis.
but here’s the thing that i find absolutely puzzling. when i tested it in your startBox2D environment, it imported perfectly. (?!)
i’ll keep at it and post any findings/solutions for future references
October 18th, 2009 at 11:20 PM
well i realize what the problem is.
in the example code i was using, they were flipping the screen with a screen.scaleY = -1;
this remedies the conflicting problem with flash’s coordinates of (X,Y) at top left corner, from Box2D’s coordinates of (X,Y) at the bottom left corner.
unfortunately, this means i either flip the parser, or flip the screen. painful -_-;
October 19th, 2009 at 5:16 PM
Yeah, that’s a slight pain to deal with. Looks like you know how to fix it though.
If you’re working on a public project you should send me a link when you get it published. It’d be nice to see something using this parser.