libgdx-tiled-maps — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited libgdx-tiled-maps (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Quick reference for libGDX Tiled map integration (com.badlogic.gdx.maps.*). Covers loading, TiledMap structure, renderers, unit scale, object layers, properties, animated tiles, and collision extraction.
// Direct loading (uses InternalFileHandleResolver by default)
TiledMap map = new TmxMapLoader().load("maps/level1.tmx");
// With parameters
TmxMapLoader.Parameters params = new TmxMapLoader.Parameters(); // 1.12.x
// BaseTiledMapLoader.Parameters params = new BaseTiledMapLoader.Parameters(); // 1.13.1+
params.textureMinFilter = Texture.TextureFilter.Linear;
params.textureMagFilter = Texture.TextureFilter.Linear;
TiledMap map = new TmxMapLoader().load("maps/level1.tmx", params);assetManager.setLoader(TiledMap.class, new TmxMapLoader(new InternalFileHandleResolver()));
assetManager.load("maps/level1.tmx", TiledMap.class);
// ... assetManager.update() in render loop ...
TiledMap map = assetManager.get("maps/level1.tmx", TiledMap.class);When using AssetManager, disposal is handled by assetManager.unload("maps/level1.tmx").
Loads tiles from a TextureAtlas instead of separate tileset images. Reduces draw calls.
Requirements:
atlas with the path to the .atlas file// Direct
TiledMap map = new AtlasTmxMapLoader().load("maps/level1.tmx");
// Via AssetManager
assetManager.setLoader(TiledMap.class, new AtlasTmxMapLoader(new InternalFileHandleResolver()));Gotcha: Throws GdxRuntimeException("The map is missing the 'atlas' property") if the TMX lacks the atlas map property.
| Field | Type | Default | Purpose |
|---|---|---|---|
flipY | boolean | true | Converts Tiled y-down to libGDX y-up |
convertObjectToTileSpace | boolean | false | Divides object coords by tile dimensions |
generateMipMaps | boolean | false | Generate mipmaps for tileset textures |
textureMinFilter | TextureFilter | Nearest | Min filter for tileset textures |
textureMagFilter | TextureFilter | Nearest | Mag filter for tileset textures |
In 1.12.x: BaseTmxMapLoader.Parameters (or TmxMapLoader.Parameters which extends it). In 1.13.1+: BaseTiledMapLoader.Parameters. TmxMapLoader.Parameters no longer exists — update imports on upgrade.
TiledMap (extends Map, implements Disposable)
├── getLayers() → MapLayers
│ ├── TiledMapTileLayer — tile grid data
│ │ └── Cell[][] → TiledMapTile
│ ├── MapLayer — object layers
│ │ └── MapObjects → MapObject subclasses
│ ├── TiledMapImageLayer — image layers
│ └── MapGroupLayer — group layers (has nested getLayers())
├── getTileSets() → TiledMapTileSets
│ └── TiledMapTileSet → TiledMapTile (by int ID)
├── getProperties() → MapProperties
└── dispose()// By index
MapLayer layer = map.getLayers().get(0);
// By name — returns null if not found (does NOT throw)
MapLayer layer = map.getLayers().get("ground");
// MUST cast to TiledMapTileLayer for tile operations
TiledMapTileLayer tileLayer = (TiledMapTileLayer) map.getLayers().get("ground");
// Filter by type (single-arg allocates new Array each call)
Array<TiledMapTileLayer> tileLayers = map.getLayers().getByType(TiledMapTileLayer.class);
// Reuse array to avoid allocation
Array<TiledMapTileLayer> fill = new Array<>();
map.getLayers().getByType(TiledMapTileLayer.class, fill);TiledMapTileLayer layer = (TiledMapTileLayer) map.getLayers().get("ground");
int cols = layer.getWidth(); // map width in tiles
int rows = layer.getHeight(); // map height in tiles
int tw = layer.getTileWidth(); // tile width in pixels
int th = layer.getTileHeight(); // tile height in pixels
// getCell() takes TILE coordinates (not pixels). Returns null if empty or out of bounds.
TiledMapTileLayer.Cell cell = layer.getCell(tileX, tileY);
if (cell != null) {
TiledMapTile tile = cell.getTile();
TextureRegion region = tile.getTextureRegion();
MapProperties tileProps = tile.getProperties();
boolean flipH = cell.getFlipHorizontally();
boolean flipV = cell.getFlipVertically();
int rotation = cell.getRotation(); // Cell.ROTATE_0, ROTATE_90, ROTATE_180, ROTATE_270
}Converting world position to tile coordinates:
// If unitScale = 1f (camera in pixel units):
int tileX = (int) (worldX / layer.getTileWidth());
int tileY = (int) (worldY / layer.getTileHeight());
// If unitScale = 1/tileWidth (camera in tile units):
int tileX = (int) worldX;
int tileY = (int) worldY;int getId();
TextureRegion getTextureRegion();
float getOffsetX(); // float, not int
float getOffsetY();
MapProperties getProperties();
MapObjects getObjects(); // tile collision shapes from Tiled editor
TiledMapTile.BlendMode getBlendMode(); // NONE or ALPHAImplementations: StaticTiledMapTile, AnimatedTiledMapTile.
MapRenderer (interface, com.badlogic.gdx.maps)
└── TiledMapRenderer (interface, extends MapRenderer)
└── BatchTiledMapRenderer (abstract, implements TiledMapRenderer + Disposable)
├── OrthogonalTiledMapRenderer
├── IsometricTiledMapRenderer (experimental)
├── IsometricStaggeredTiledMapRenderer
└── HexagonalTiledMapRenderer// Creates internal SpriteBatch — renderer owns it, dispose() releases it
new OrthogonalTiledMapRenderer(map) // unitScale = 1.0f
new OrthogonalTiledMapRenderer(map, unitScale)
// Uses external Batch — renderer does NOT dispose it
new OrthogonalTiledMapRenderer(map, batch) // unitScale = 1.0f
new OrthogonalTiledMapRenderer(map, unitScale, batch)The batch parameter accepts any Batch implementation (the interface), not just SpriteBatch.
renderer.setView(camera); // MUST call before render()
renderer.render(); // renders all layers
// Selective layer rendering (by index) for sprite interleaving:
int[] bgLayers = {0, 1};
int[] fgLayers = {2, 3};
renderer.setView(camera);
renderer.render(bgLayers);
batch.setProjectionMatrix(camera.combined);
batch.begin();
// ... draw player sprites ...
batch.end();
renderer.render(fgLayers);`setView()` overloads:
setView(OrthographicCamera camera) — computes projection + view bounds from camerasetView(Matrix4 projection, float x, float y, float width, float height) — explicit bounds`getBatch()` returns the renderer's Batch (return type is Batch interface, not SpriteBatch).
BatchTiledMapRenderer.renderObject(MapObject) is intentionally a no-op. Object layers are not rendered visually. They exist only as data for collision, spawn points, triggers, etc. Tile objects placed in object layers will NOT appear on screen.
unitScale controls how tileset pixels map to world units: worldUnits = pixels * unitScale.
| unitScale | Meaning | Camera setup example |
|---|---|---|
1f | 1 pixel = 1 world unit | camera.setToOrtho(false, 800, 480) |
1/32f | 32 pixels = 1 world unit | camera.setToOrtho(false, 25, 15) — sees 25x15 tiles |
1/16f | 16 pixels = 1 world unit | camera.setToOrtho(false, 50, 30) |
float unitScale = 1 / 32f;
OrthogonalTiledMapRenderer renderer = new OrthogonalTiledMapRenderer(map, unitScale);
OrthographicCamera camera = new OrthographicCamera();
camera.setToOrtho(false, 30, 20); // sees 30x20 tilesWhen unitScale != 1f, object layer coordinates are still in pixels by default. Scale them yourself or set convertObjectToTileSpace = true in loader parameters.
| Class | Accessor | Returns |
|---|---|---|
RectangleMapObject | getRectangle() | Rectangle |
EllipseMapObject | getEllipse() | Ellipse |
CircleMapObject | getCircle() | Circle |
PolygonMapObject | getPolygon() | Polygon |
PolylineMapObject | getPolyline() | Polyline |
PointMapObject | getPoint() | Vector2 |
TiledMapTileMapObject | getTile() | TiledMapTile (extends TextureMapObject) |
All inherit from MapObject which provides getName(), getProperties(), getColor(), getOpacity(), isVisible().
MapLayer collisionLayer = map.getLayers().get("collision");
MapObjects objects = collisionLayer.getObjects();
for (MapObject object : objects) {
if (object instanceof RectangleMapObject) {
Rectangle rect = ((RectangleMapObject) object).getRectangle();
// rect.x, rect.y = bottom-left in pixels (with default flipY=true)
} else if (object instanceof PolygonMapObject) {
float[] verts = ((PolygonMapObject) object).getPolygon().getTransformedVertices();
} else if (object instanceof PolylineMapObject) {
float[] verts = ((PolylineMapObject) object).getPolyline().getTransformedVertices();
} else if (object instanceof EllipseMapObject) {
Ellipse ellipse = ((EllipseMapObject) object).getEllipse();
}
}
// Filter by type (2-arg overload reuses array):
Array<RectangleMapObject> rects = new Array<>();
objects.getByType(RectangleMapObject.class, rects);Tiled uses y-down (origin top-left). libGDX uses y-up (origin bottom-left). With default flipY = true, the loader converts coordinates automatically:
y = mapHeightPx - tiledY - objectHeight (bottom-left corner)convertObjectToTileSpace = trueDo NOT manually flip Y when `flipY = true` (the default). Coordinates are already converted.
float scale = 1 / 32f; // match renderer unitScale
for (RectangleMapObject rmo : objects.getByType(RectangleMapObject.class)) {
Rectangle rect = rmo.getRectangle();
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.StaticBody;
// rect (x,y) is bottom-left; Box2D setAsBox uses center + half-dimensions
bodyDef.position.set(
(rect.x + rect.width / 2) * scale,
(rect.y + rect.height / 2) * scale
);
PolygonShape shape = new PolygonShape();
shape.setAsBox(rect.width / 2 * scale, rect.height / 2 * scale);
Body body = world.createBody(bodyDef);
body.createFixture(shape, 0f);
shape.dispose();
}MapProperties appears on maps, layers, tiles, and objects.
MapProperties props = object.getProperties();
// Raw (returns Object, null if missing)
Object val = props.get("name");
// Typed (ClassCastException if wrong type, null if missing)
String name = props.get("name", String.class);
// Typed with default — signature: get(key, defaultValue, clazz)
int hp = props.get("hp", 100, Integer.class);
float speed = props.get("speed", 1.0f, Float.class);
boolean solid = props.get("solid", false, Boolean.class);
props.containsKey("name"); // boolean
props.put("custom", 42);Tiled type mapping: int→Integer, float→Float, bool→Boolean, string→String, color→Color.
The loader also stores standard attributes on objects: x, y, width, height, type, id, rotation.
Animations defined in Tiled's tileset editor are loaded automatically — no manual setup needed.
// Manual creation (rare — usually auto-loaded from TMX):
// Uniform duration (interval in seconds, converted to ms internally)
new AnimatedTiledMapTile(0.25f, frameTiles); // Array<StaticTiledMapTile>
// Per-frame durations (in milliseconds)
new AnimatedTiledMapTile(intervals, frameTiles); // IntArray, Array<StaticTiledMapTile>Gotchas:
AnimatedTiledMapTile.updateAnimationBaseTime() in beginRender(). You cannot offset individual tile animations.setTextureRegion(), setOffsetX(), setOffsetY() throw GdxRuntimeException on animated tiles.StaticTiledMapTile instances.map.dispose(); // disposes owned tileset textures
renderer.dispose(); // disposes batch ONLY if renderer created it (ownsBatch)
// Via AssetManager:
assetManager.unload("maps/level1.tmx"); // handles everythingDo NOT dispose tileset textures separately. TmxMapLoader's textures are owned by TiledMap — calling texture.dispose() on them causes use-after-free when map.dispose() runs.
If you passed an external Batch to the renderer constructor, dispose it yourself — the renderer won't.
layer.getCell(x, y).getTile(). Always null-check getCell() — returns null for empty or out-of-bounds cells.1.0f (pixels). Pass 1f / tilePixelSize if your camera operates in tile/world units.MapLayer. Cast to TiledMapTileLayer for getCell() access.renderObject() is intentionally empty. Object layers are data-only. Tile objects in object layers are NOT drawn.convertObjectToTileSpace = false means getRectangle() etc. return pixel coordinates. Scale by unitScale to match your world.map.dispose().flipY = true, coordinates are already converted. Flipping again puts objects in the wrong position.getByType(Class) allocates a new Array each call. Cache at load time or use the two-arg overload.SpriteBatch, you must call renderer.dispose() to release it.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.