I have made a class which allows you to create Box2D bodies for a object layer. I have made this because I have needed this myself and this would be a huge time saver for me and I hope for you also.
This class supports the following objects,
Rectangle
Circle (You can create it with any shape set “Type” parameter to “Circle” and it creates a Circle fixture based on width as diameter)
Polygon (Allowed only per 8 points, I you need more then you have to split it up)
Polyline
How it works:
In you tmx editor you have to create a layer called “Collision” and make all the objects in that layer.
Change in TiledBodyCreator.cpp #define PTMRATIO 64
To your ptmratio
Then in the game code you have to include TiledBodyCreator.h and after you have created Tilemap and Box2d world you can add this code which will create a body with all objects as fixtures in it and add it to world.
TiledBodyCreator::initCollisionMap(map, world);
Here is a small preview of what I have tested
My tiled map in editor
My game with box2d debug
This is the code I have used
this->setAnchorPoint(Point::ZERO);
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
auto world = new b2World(gravity);
world->SetAllowSleeping(false);
auto debugLayer = B2DebugDrawLayer::create(world, 64);
this->addChild(debugLayer, 9999);
auto map = TMXTiledMap::create("Test.tmx");
map->setAnchorPoint(Point::ZERO);
map->setPosition(0,0);
this->addChild(map, 3);
TiledBodyCreator::initCollisionMap(map, world);
this->setScale(0.2f);
I hope this helped you a lot. The classes are added to attachments of this post.
Hey, sorry for a late reaction.
Could you mayby check if your tiledmap contains empty polylines or mayby open your tmx file with a text editor and send me collision layer part of it. You can also try to replace createPolyline method in TiledBodyCrator.cpp with this and send me the log:
FixtureDef* TiledBodyCreator::createPolyline(ValueMap object)
{
ValueVector pointsVector = object["polylinePoints"].asValueVector();
auto position = Point(object["x"].asFloat() / PTMRATIO, object["y"].asFloat() / PTMRATIO);
CCLOG("Size of pointsVector: %f", pointsVector.size());
b2ChainShape *polylineshape = new b2ChainShape();
float verticesSize = pointsVector.size()+ 1;
b2Vec2 vertices[30];
int vindex = 0;
auto fix = new FixtureDef();
for(Value point : pointsVector) {
CCLOG("Initializing vector at index: %d", vindex);
vertices[vindex].x = (point.asValueMap()["x"].asFloat() / PTMRATIO + position.x);
vertices[vindex].y = (-point.asValueMap()["y"].asFloat() / PTMRATIO + position.y);
vindex++;
}
polylineshape->CreateChain(vertices, vindex);
fix->fixture.shape = polylineshape;
return fix;
}
Thanks for this tweak. Here is the version with your changes I am sure it will help a lot of people :). I am also glad to hear its useful to you.TileBodyCreator.zip (1.8 KB)
The bunny is 128 x 128 pixel.
The location is 640, 360.
I divided the location with PTM_RATIO 128, but still doesn’t match.
It doesn’t align with box2D rect.
I know now. This is a bug.
When I tried to use circle, the circle aligns perfectly with the image (bunny).
Problem solved now.
If I used rectangle, I need to multiply the PTM_RATIO to match the body position.
Hi,
I would like to share my experience using this class, for help anyone else
[+] The body in this class (for the rect, circle, polyline, polygon) is static (as default), so, if you want to generate collisions be sure to set to dynamic your other bodys. Remember: in box2d, static with static not collides. For move the dynamic body i recommend to use SetLinearVelocity.
[+] If you want to see the fixtures/shapes drawn in the map, you will need to debug using box2d. Here i share a class that works for me using tiled maps + box2d debug: b2DebugDraw.zip (3.7 KB) (thanks @stevetranby). Don’t forget to set the global Z order. Example:
auto debugLayer = B2DebugDrawLayer::create(_world, SCALE_RATIO);
debugLayer->setGlobalZOrder(99);
tileMap->addChild(debugLayer);
[+] I had problems with the sizes in polylines and polygons using cocos2d-x 3.17 and iPhone 7. I discovered that this problem is because the methods are not using content scale factor (CC_CONTENT_SCALE_FACTOR()) in polyline and polygon points. So, i recommend to fix this problem in both methods. This is the final versions that i’m using and it works for me:
FixtureDef* TiledBodyCreator::createPolygon(ValueMap object)
{
ValueVector pointsVector = object["points"].asValueVector();
auto position = Point(object["x"].asFloat() / PTMRATIO, object["y"].asFloat() / PTMRATIO);
b2PolygonShape *polyshape = new b2PolygonShape();
b2Vec2 vertices[b2_maxPolygonVertices];
int vindex = 0;
if(pointsVector.size() > b2_maxPolygonVertices) {
CCLOG("Skipping TMX polygon at x=%d,y=%d for exceeding %d vertices", object["x"].asInt(), object["y"].asInt(), b2_maxPolygonVertices);
return NULL;
}
auto fix = new FixtureDef();
for(Value point : pointsVector) {
vertices[vindex].x = ((point.asValueMap()["x"].asFloat() / PTMRATIO)/CC_CONTENT_SCALE_FACTOR() + position.x);
vertices[vindex].y = ((-point.asValueMap()["y"].asFloat() / PTMRATIO)/CC_CONTENT_SCALE_FACTOR() + position.y);
vindex++;
}
polyshape->Set(vertices, vindex);
fix->fixture.shape = polyshape;
return fix;
}
FixtureDef* TiledBodyCreator::createPolyline(ValueMap object)
{
ValueVector pointsVector = object["polylinePoints"].asValueVector();
auto position = Point(object["x"].asFloat() / PTMRATIO, object["y"].asFloat() / PTMRATIO);
b2ChainShape *polylineshape = new b2ChainShape();
int verticesCapacity=32;
b2Vec2* vertices = (b2Vec2*)calloc(verticesCapacity, sizeof(b2Vec2));
int vindex = 0;
auto fix = new FixtureDef();
for(Value point : pointsVector) {
if(vindex>=verticesCapacity)
{
verticesCapacity+=32;
vertices = (b2Vec2*)realloc(vertices, verticesCapacity*sizeof(b2Vec2));
}
vertices[vindex].x = ((point.asValueMap()["x"].asFloat() / PTMRATIO)/CC_CONTENT_SCALE_FACTOR() + position.x);
vertices[vindex].y = ((-point.asValueMap()["y"].asFloat() / PTMRATIO)/CC_CONTENT_SCALE_FACTOR() + position.y );
vindex++;
}
polylineshape->CreateChain(vertices, vindex);
fix->fixture.shape = polylineshape;
return fix;
}