Skip to main content

Tiled Map Requirements

Hs2d uses Tiled maps as authoring files, but the Roku runtime expects a small canonical JSON subset. Keep the workflow explicit:

Tiled authoring and preparation flowing through runtime level loading, asset readiness, world-builder layer branching, and retained gameplay stateTiled authoring and preparation flowing through runtime level loading, asset readiness, world-builder layer branching, and retained gameplay state

Raw Tiled exports are allowed to contain editor metadata, encoded tile data, and external JSON tilesets. Prepared runtime JSON is the normalized shape that Hs2dLevelLoader reads on device.

Prep Command

Run the prep command before adding a map to an asset bundle:

npx hst game:prep-level levels/level-1.json -o asset-bundles/my-game/levels/level-1.json
npx hst game:prep-level levels/level-1.json --pretty

The prep tool accepts JSON map files, including .tmj content when it is JSON. It does not parse XML maps or XML tilesets.

Raw Export Vs Runtime JSON

ConcernRaw Tiled exportPrepared runtime JSON
Tile dataPlain numeric JSON arrays with encoding absent, or base64 strings with optional zlib/gzip compression. An explicit encoding: "csv" is not accepted by the prep tool.Plain numeric data arrays only. No encoding or compression.
TilesetsEmbedded tilesets or external JSON tilesets.One embedded active tileset, with unused tilesets removed.
External TSXTiled can reference XML .tsx.Rejected. Save as JSON .tsj or embed the tileset.
Editor fieldsMay include version, tiledversion, compressionlevel, nextlayerid, nextobjectid, and editorsettings.Stripped. Runtime does not need editor-only fields.
Infinite/chunked mapsTiled can export chunked data for infinite maps.Rejected. Runtime supports finite dense maps only.
Flipped/rotated tilesTiled stores flip flags in high gid bits.Rejected. Add pre-flipped tiles to the tileset instead.

The prep step does not replace runtime validation. It creates the canonical shape, and parseTiledJsonLevel still rejects unsupported input if a bad file reaches runtime.

Supported Runtime Subset

A runtime map must satisfy all of these rules:

RequirementRuntime behavior
type is absent or mapAny other type is rejected.
orientation is orthogonalIsometric, staggered, hexagonal, and missing orientation are rejected.
infinite is absent or falseInfinite maps are rejected.
width, height, tilewidth, and tileheight are positive integersMissing, zero, fractional, or negative dimensions are rejected.
layers is an arrayMissing layer arrays are rejected.
Tile layers contain a dense data array with map width * height entriesEncoded, compressed, chunked, or wrong-length tile layers are rejected. The runtime parser validates against map dimensions, not layer-local dimensions.
Tile gids do not contain flip flagsAny gid >= 2^28 is rejected.
Zero or one tileset is presentMore than one tileset is rejected by runtime. Building tile layers requires tileset metadata.
Layer names are stable idsHs2dLevel uses Tiled layer name, not numeric id, as the lookup id.

parseTiledJsonLevel flattens group layers recursively. Native group parallax, offsets, visibility, and opacity are composed into child layers:

  • parallax is multiplied down the group tree
  • offsets are added
  • visibility is inherited with logical AND
  • opacity is multiplied and preserved on parsed layers

The world builder uses visibility to skip layers. Opacity is parsed for callers, but the current builder paths do not apply layer opacity during rendering.

Layer Types

Tiled layer typeRuntime support
imagelayerParsed as an image layer. Built as sky, direct parallax, or surface parallax depending on properties.
tilelayerParsed as tile kinds. Built as dynamic, static, or cached tile rendering.
objectgroupParsed as objects. Built as decor or entities, or left as data-only zones.
groupFlattened by the parser; group transform state is composed into children.

Other layer types are ignored by the parser because they do not match the supported discriminated union.

Tilesets And Tile Kinds

Runtime tile ids are rebased from Tiled gids:

Tiled gid 0          -> tile kind -1 (empty)
Tiled gid firstgid -> tile kind 0
Tiled gid firstgid+1 -> tile kind 1

Hs2dLevel exposes tile metadata through:

APIPurpose
getTileKinds(layerId)Dense tile-kind array for a tile layer.
getTileProperties(tileKind)Custom tileset properties for a tile kind.
getTileAnimation(tileKind)Tiled animation frames as { tileKind, durationMs }.
isSolidTile(x, y, layerId = 'terrain')World collision helper by tile coordinate.
isTileKindSolid(tileKind)Solidity helper for a tile kind.

Solidity uses a compatibility rule. If any tileset tile declares a solid property, only tiles with solid: true or solid: "true" are solid. If no tiles declare solid, any non-empty tile is treated as solid.

Object Shape

Objects are normalized into a stable Hs2dLevelObject shape:

FieldRuntime value
idString form of Tiled object id, or the object index when no id exists.
nameTiled object name, default ''.
typeTiled object type, default ''. Entity bindings use this value as the binding key. If your Tiled UI labels this as class, confirm the exported JSON still has a type field or normalize it before runtime.
x, y, width, heightTiled world coordinates, defaulting missing values to 0.
centerX, centerYDerived from x/y/width/height.
propertiesFlattened custom properties as a plain object.

The parser does not currently preserve Tiled object rotation, polygon geometry, text, ellipse state, or tile object gid in Hs2dLevelObject. Use rectangular point/area objects for entities and zones.

Prep Rejection Cases

hst game:prep-level rejects:

CaseReason
Invalid JSONThe prep tool only reads JSON/TMJ content.
Non-map typeHs2d levels are Tiled maps.
Non-orthogonal orientationRuntime only supports orthogonal maps.
Infinite maps or chunked layersRuntime requires finite dense arrays.
Missing layersRuntime requires a layer array.
Unsupported tile encodingOnly plain arrays and base64 strings are normalized.
Unsupported compressionBase64 with none, zlib, or gzip is supported; zstd and others are rejected.
Corrupt base64 dataTile data must decode to 32-bit little-endian gids.
Wrong data lengthAt prep time, tile count must equal layer.width * layer.height. A normally exported finite map uses map-sized layers.
Flip flags on tile layers or tile objectsRuntime has no flip/rotate path.
External .tsx tilesetsSave as JSON .tsj or embed in the map.
More than one used tilesetRuntime supports one active tileset per map.

Unused tilesets are dropped with warnings. External JSON tilesets are inlined relative to the map path.

Runtime Rejection Cases

parseTiledJsonLevel rejects:

CaseError class
Empty or invalid parsed map valueInvalid level JSON.
Non-map typeInvalid map type.
Orientation other than orthogonalUnsupported map orientation.
infinite: trueUnsupported finite-map contract.
Invalid map/tile dimensionsInvalid dimensions.
Missing layers arrayMissing layers.
Tile layer chunksUnsupported chunked layer.
Tile layer encoding or compressionUnsupported encoded/compressed runtime data.
Tile layer data is not an arrayUnsupported runtime data.
Tile layer data length mismatchInvalid data length.
Gid flip flagsUnsupported flip flags.
More than one tilesetUnsupported multiple tilesets.

The prep and runtime checks differ slightly: prep validates against each tile layer's declared dimensions, while runtime validates every tile layer against the map dimensions. Keep finite tile layers map-sized so a file cannot pass prep and then fail at runtime.

Do And Don't

Do:

  • author finite orthogonal maps
  • keep layer names stable because they are runtime ids
  • use one combined tileset image per map
  • run hst game:prep-level as part of the asset pipeline
  • express collision through tileset solid properties
  • use object layers for entities, decor, spawn points, hazards, triggers, and other world data

Don't:

  • load raw base64/compressed Tiled output directly on device
  • rely on Tiled flip or rotate flags
  • use multiple active tilesets in one map
  • put runtime decisions in editor-only fields
  • query raw JSON in frame loops
  • expect object rotation, polygons, or tile object gids to be available in Hs2dLevelObject

Source Reference

SourceConfirms
../hosanna-tools/src/support-tools/tiled-prep.tsPrep normalization, external tileset handling, warnings, and prep-time rejection cases.
../hosanna-tools/src/support-tools/tiled-prep.test.tsTested prep cases for base64, zlib, gzip, external .tsj, TSX rejection, flip flags, and multiple tilesets.
../games/hosanna-ui/src/hosanna-game/hosanna2d/level/Hs2dTiledLevel.tsRuntime parser, supported map subset, group flattening, object normalization, tileset parsing, and runtime rejection cases.
../games/hosanna-ui/src/hosanna-game/hosanna2d/level/Hs2dTiledLevel.test.tsParser behavior for tile rebasing, layer order, image layers, group composition, custom properties, animations, and rejection cases.
../games/hosanna-ui/src/hosanna-game/hosanna2d/level/Hs2dLevel.tsLevel APIs, property helpers, object lookup, parallax sprite specs, and tile solidity semantics.
Talk to us