for a platformer game, i create my levels as pixel graphic files. Since the tracing algorithm for images in RUBE is not able to produce correct fixtures out of my bitmaps, i wrote a small script that imports GeoJSON files that i get from potrace. Potrace is a very good bitmap-to-vector software, available for all major OSes.
My workflow, so you get an idea how to use it:
- create bmp with the desired level layout
- run "potrace -i -b geojson level.bmp" on it; that creates a new file level.json
- execute my rubescript, select the .json-file from potrace
- insert items, enemies etc. in RUBE
- load the RUBE json in my game.
Limitations:
- since RUBE doesn't support holes in vertices, GeoJSON files with holes won't work.
- the coordinates in the GeoJSON file refer to pixels, not Box2D meter. The fixtures created will be very large, you have to scale them down afterwards.
- i haven't tested the script thoroughly, it may or may not work for your files. I use it only with my level files and with potrace and no other GeoJSON file.
- the program can crash, if poly2tri get's overstrained by the imported polygons.
[It would be supercool if RUBE would support holes in fixtures and/or the potrace library (it's GPLed, don't know if that's the problem).]
![Wink ;)](./images/smilies/icon_e_wink.gif)
[and my scripting style is bad. sorry.]
Code: Select all
void main()
{
string filename = queryOpenFile("*");
string filecontent = readFile(filename);
vec2[][] polygons = parseGeoJSON(filecontent);
print (polygons.length() + " polygons found");
body b = addBody(-1, '{"type":"static","awake":true}');
b.setPos( cursor() );
for (uint i = 0; i < polygons.length(); ++i)
{
string xValues, yValues;
for (uint j = 0; j < polygons[i].length()-1; j++) //last coordinate is first coordinate again, so we skip it
{
xValues += polygons[i][j].x;
yValues += polygons[i][j].y;
if (j < polygons[i].length() - 2)
{
xValues += ",";
yValues += ",";
}
}
if (xValues.isEmpty() || yValues.isEmpty()) continue;
string fixtureDef = '{"density":1,"shapes":[{"radius":0,"type":"polygon"}],"friction":0.2,"vertices":{"x":['+xValues+'],"y":['+yValues+']}}';
b.addFixture(-1, fixtureDef);
xValues = ""; yValues = "";
}
print("ok");
}
uint filePointer = 0;
vec2[][] parseGeoJSON(string jsonfilecontent)
{
vec2[][] polygons;
while (filePointer < jsonfilecontent.length())
{
string char = readChar(jsonfilecontent);
if (char == "\"")
{
string word = readWord(jsonfilecontent, "\"");
if (word == "coordinates")
{
polygons = readPolygon(jsonfilecontent, polygons);
}
}
}
return polygons;
}
string readChar(string filecontent)
{
if (filePointer >= filecontent.length())
{
print("end of file");
return "";
}
//skip blanks und newlines
string ret = " ";
while (ret == " " || ret == "\n")
{
filePointer++;
ret = filecontent.substr(filePointer-1,1);
}
return filecontent.substr(filePointer-1,1);
}
string readWord(string filecontent, string delim)
{
string word = "";
string char = readChar(filecontent);
while (char != delim)
{
word += char;
char = readChar(filecontent);
}
return word;
}
vec2[][] readPolygon(string filecontent, vec2[][] polys)
{
int polycount = 0;
// fast forward to beginning of polygon
while (readChar(filecontent) != "[") continue;
vec2[] polygon;
//readCoordinate reads whole coord-block: xx.0,yy.0] incl. closing bracket
while (readChar(filecontent) != "]") //... and that's why this ] marks the end of the polygon
{
polygon.insertLast(readCoordinate(filecontent));
polycount++;
}
polys.insertLast(polygon);
if (readChar(filecontent) == ",") // multiple polygons in one definition
return readPolygon(filecontent, polys);
else return polys;
}
vec2 readCoordinate(string filecontent)
{
readChar(filecontent); // skip "["
string word = readWord(filecontent, "]");
vec2 coordinate;
string[] coordinates = word.split(",");
if (coordinates.length() != 2) return coordinate;
float x = parseFloat(coordinates[0]);
float y = parseFloat(coordinates[1]);
coordinate.set(x,y);
return coordinate;
}