libGDX Tiled Box2D - tile collisions

25th March 2021, 15:00:00

Intro

This is the object layer sub page of the example. If you are new to the series, click here to access the main page first.

Map Setup

  1. Open tiles settings
  2. Open the tile collision editor
  3. Draw the tile collisions. Hold ctrl to snap to corners.

Comparison

Object layer

Tile collisions

As you see, the object layer version creates a lot less rectangles, therefore is faster to parse and might work better. The tile collision version however is automated and you don’t need to care about creating collisions shapes.

Implementation

Now for the fun part!

Let’s iterate through each tile and check if it has a collision object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
TiledMapTileLayer layer = (TiledMapTileLayer) map.getLayers().get(0);
for (int x = 0; x < layer.getWidth(); x++)
{
for (int y = 0; y < layer.getHeight(); y++)
{
TiledMapTileLayer.Cell cell = layer.getCell(x, y);
if (cell == null)
continue;

MapObjects cellObjects = cell.getTile().getObjects();
if (cellObjects.getCount() != 1)
continue;

MapObject mapObject = cellObjects.get(0);

Then we’ll need to get the actual shape in the map object.
This is done by checking which class it actually is (then converting to it), and calling .getXXX() on it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (mapObject instanceof RectangleMapObject)
{
RectangleMapObject rectangleObject = (RectangleMapObject) mapObject;
Rectangle rectangle = rectangleObject.getRectangle();
}
else if (mapObject instanceof EllipseMapObject)
{
EllipseMapObject circleMapObject = (EllipseMapObject) mapObject;
Ellipse ellipse = circleMapObject.getEllipse();
}
else if (mapObject instanceof PolygonMapObject)
{
PolygonMapObject polygonMapObject = (PolygonMapObject) mapObject;
Polygon polygon = polygonMapObject.getPolygon();
}

Box2d body creation

Now inside each if …

Rectangle

This is a bit more complicated compared to the object layer way.

In Tiled every object’s x/y coordinates are bottom left. Sadly Box2D uses center coordinates. Therefore we’ll need to some magic to convert between them.

  1. Convert x * tileSize so we get the bottom left “tile pixel coordinates” of the tile.
  2. Add tileSize / 2f to that, so we’re centered on the tile.
  3. Then we’ll do some magic to get the proper offset + rectangle.getY() - (tileSize - rectangle.getHeight()) / 2f
1
2
if (mapObject instanceof RectangleMapObject) {
BodyDef bodyDef = getBodyDef(x * tileSize + tileSize / 2f + rectangle.getX() - (tileSize - rectangle.getWidth()) / 2f, y * tileSize + tileSize / 2f + rectangle.getY() - (tileSize - rectangle.getHeight()) / 2f);

.setAsBox() accepts half width/height parameters, therefore we’ll need to half the width/height.

1
2
3
4
5
Body body = world.createBody(bodyDef);
PolygonShape polygonShape = new PolygonShape();
polygonShape.setAsBox(rectangle.getWidth() / 2f, rectangle.getHeight() / 2f);
body.createFixture(polygonShape, 0.0f);
polygonShape.dispose();

Circle

Here we have another little problem, Tiled allows ellipses, Box2D only circles. Therefore we’ll need to check if the ellipse is actually a circle.

Additionally ellipse coordinates are already centered, so we don’t have that problem here.

1
2
else if (mapObject instanceof EllipseMapObject) {
BodyDef bodyDef = getBodyDef(x * tileSize + tileSize / 2f + ellipse.x, y * tileSize + tileSize / 2f + ellipse.y);

Then we’ll check if it’s a circle, if not we’ll exit.

1
2
if (ellipse.width != ellipse.height)
throw new IllegalArgumentException("Only circles are allowed.");
1
2
3
4
5
Body body = world.createBody(bodyDef);
CircleShape circleShape = new CircleShape();
circleShape.setRadius(ellipse.width / 2f);
body.createFixture(circleShape, 0.0f);
circleShape.dispose();

Polygon

Polygons are probably the most easiest. However you can only load polygons with a maximum of 8 points.

1
2
else if (mapObject instanceof PolygonMapObject) {
BodyDef bodyDef = getBodyDef(x * tileSize + polygon.getX(), y * tileSize + polygon.getY());
1
2
3
4
5
Body body = world.createBody(bodyDef);
PolygonShape polygonShape = new PolygonShape();
polygonShape.set(polygon.getVertices());
body.createFixture(polygonShape, 0.0f);
polygonShape.dispose();

That’s pretty much everything you need!

Repo

https://github.com/lyze237-examples/LibgdxTiledBox2DExample