tilemap-parserv5.2.0GITHUB

Map Parsing & Rendering

Loading a map is one call; rendering is one call per frame. load_map() parses the tilemap-editor JSON into a TilemapData, and TileLayerRenderer draws it with chunked culling.

LOADING MAPS

load_map.pyPYTHON
from tilemap_parser import load_map

game_data = load_map("data/map.json")

print(f"Map size: {game_data.map_size}")     # (cols, rows)
print(f"Tile size: {game_data.tile_size}")   # (w, h), effective px
print(f"Render scale: {game_data.render_scale}")

# Layers, in draw order, filtered by type if you want:
for layer in game_data.get_layers(layer_type="tile"):
    print(layer.name, layer.z_index)

The raw parsed structure lives on game_data.parsed (see the API reference's parsed data classes). For collision you usually want the layer as a flat grid: game_data.build_tile_map() collapses the tile layers into {(col, row): tile_id}, which is what PhysicsWorld owns.

TILE RENDERING

TileLayerRenderer draws only the visible chunks, respects layer z_index and y_sort, and reports what it did. The camera offset is passed per call; nothing is stored:

game loopPYTHON
from tilemap_parser import TileLayerRenderer

renderer = TileLayerRenderer(game_data)

# In your game loop:
stats = renderer.render(screen, camera.offset)
# stats: LayerRenderStats: drawn_tiles, skipped_tiles, visible_layers
  • render(target, camera_xy, viewport_size=None, *, extra_objects=None, current_time_ms=None): current_time_ms drives tileset animations.
  • extra_objects lets you blit sprites in the same pass (any objects with surface, x, y), after the tile layers.
  • warm_cache() pre-bakes every tile variant up front, then frees the source data.

RENDERING & COLLISION OVERLAYS

The renderer is collision-blind: it draws tile textures from each layer's ttype / variant and has no idea whether a tile is solid, one-way, or air. Collision facts live in the world — world.tile_map plus the collision tileset. The split is deliberate: render everything, collide with a subset (exclude_layers only affects the world).

Want debug overlays — say, dashed edges on one-way platforms? Ask the world, not the renderer:

overlay.pyPYTHON
tile_id = world.tile_map.get((tx, ty))          # world id space
if tile_id is None:
    continue
tile_data = world.tileset_collision.tiles.get(tile_id)
if tile_data is None:
    continue
if any(s.one_way for s in tile_data.shapes):
    # dashed top edge at (tx * world.tile_size[0], ty * world.tile_size[1])

one_way is authored per polygon in the collision JSON — never in the map JSON — and the platformer family honors it automatically (blocks from above, passes from below), so the query is for visuals only. Two id-space caveats:

  • The renderer keys tiles by layer ttype; the world keys by the collision tile id. Same map, same ints — until use_gids=True makes the world's ids global and the two spaces diverge. Always query through world.tile_map, never through the renderer.
  • Tiles with no collision entry draw fine but collide as air; layers in exclude_layers draw fine but don't exist for physics.

EXTRACTING OBJECTS

Object layers become MapObjects: pre-scaled surfaces, positions and collision shapes, ready to feed an ObjectCollisionManager. load_map_objects takes the map and a directory containing the matching .object_collision.json files:

objects.pyPYTHON
from tilemap_parser import load_map_objects, ObjectCollisionManager

objects = load_map_objects(game_data, "data/object_collision")

manager = ObjectCollisionManager()
for obj in objects:
    manager.add_object(obj)

player_start = next((o for o in objects if o.name == "PlayerStart"), None)
if player_start is not None:
    player.x, player.y = player_start.x, player_start.y
  • Every object layer is iterated; there is no per-layer filter. The first region's layer/mask are adopted by the object.
  • require_collision=True (the default) returns only objects that have matching collision regions; pass False to also get visual-only objects (with empty shapes).
  • All spatial data is pre-scaled by the map's render_scale; no scaling on your side.

BACKGROUND (IMAGE) LAYERS

Image layers hold a single external image — a parallax sky, backdrop, or full-screen art. They carry no tiles or objects, just image_path and image_rect. The parser parses all image-layer metadata as ParsedLayer with layer_type == "image" (aliases "background" / "background_layer" are also accepted) but TilemapData.load eagerly loads only the first image layer into TilemapData.background_layer; additional image layers remain in data.parsed.layers for manual loading.

background.pyPYTHON
data = load_map("data/map.json")

bg = data.background_layer      # BackgroundLayer | None
if bg is not None and bg.surface is not None:
    # bg.image_path, bg.image_rect (x,y,w,h), bg.surface
    pos = (bg.image_rect[0], bg.image_rect[1]) if bg.image_rect else (0, 0)
    screen.blit(bg.surface, pos)

# Or query any image layer directly:
for layer in data.get_layers(layer_type="image"):
    print(layer.name, layer.image_path, layer.image_rect)
  • image_path: project-relative path; resolved against the map directory (and extra_search_base if given). Missing files produce a warning and surface == None only when skip_missing_images is enabled (default true); when skip_missing_images=False, loading raises MapParseError.
  • image_rect: pixel rect (x, y, w, h) where the image is drawn. None when not authored.
  • Only the first image/background layer is exposed as background_layer; all are still in data.parsed.layers.