I'm using Box2D for a Game Programming course, and I was wondering if it is possible to choose the best shape and size of shape for the rigidbody/fixture for a sprite based on its transparency.
I've tried to build with the idea of new content being easy to add (all data for enemies, terrain, etc. is stored in XML documents). Because of this, there will not be a new class for every enemy. This makes creating the fixture shapes difficult, since it has to be a generic solution but still map realistically to the sprite when the game is being played. The best solution I've come up with so far is to somehow figure out where the majority of the non transparent pixels are in the sprite and build the shape from that.
Does something like this exist? If not, would it be hard to implement?
Answer
I tried this. It was hard, but I did it.
Left is GIMP, top-right is a Box2D debug renderer, bottom-right is a build shell
Code repository for reference
The full code is on github here. It's scattered in a whole lot of files, so it's a bit big to put here. See below for an explanation of the technique.
I used ImageMagick, Potrace, Node.js (with poly2tri.js and simplify-js), and two evenings.
This is possible also with other tools, so I'll go over the steps in a general way:
Draw an input image.
Extract the alpha channel, because we only care about the shape.
convert infile.png -alpha extract outfile.png
Threshold the alpha (push everything under 75% opacity to 0% and the rest to 100%)
convert infile.png -threshold 75% -negate outfile.png
This gets rid of the barely visible parts.
I also negated the image because the next step's Potrace understands black pixels as stuff it should be tracing.
Trace to a vector format.
With the right options, Potrace outputs an easily readable PostScript file.
potrace --longcoding --postscript --tight --alphamax=0 \
--cleartext --output=outfile.ps infile.bmpI wrote a fairly uninteresting Node.js script to parse the path data out of that and into a JSON data structure.
I threw the JSON at D3.js to see what the path data looks like. Sure enough…
Simplify and triangulate the path.
Again a pretty simple Node.js call through simplify, then poly2tri.
Put it in Box2D!
Create a body for each set of triangles, and add the triangles as fixtures. Remember to handle each triangle's vertices in counter-clockwise order.
Success!
Sometimes your images have holes, like the One Ring here—
Thanks user Xander at Wikimedia!
The PostScript path data represents such situations as a sequence of paths (rlineto
/moveto
-commands followed by a closepath
), such that the first path is the enclosing one, and all the rest are holes. That group of paths is then terminated by a fill
command.
It looks somewhat like this:
moveto
rlineto
rlineto
[ ... more rlineto ... ]
closepath
moveto
rlineto
rlineto
[ ... more rlineto ... ]
closepath
fill
Here's how it looks when parsed, simplified, and plotted:
The blue path is the enclosing one. The red is the hole.
An image might have arbitrarily many holes:
poly2tri has methods that allow you to add holes to the generated path, as any other Delaunay triangulation library worth its salt should.
And finally, the ring in Box2D for good measure:
No comments:
Post a Comment