Skip to main content

Dynamic Tile Layer

Dynamic tiles have two retained implementations:

  • Hs2dTileLayer keeps a fixed pool of compositor sprites for the visible camera window plus padding.
  • Hs2dRingTileLayer keeps a bounded tile surface and repaints only ring slots exposed as the camera crosses tile boundaries.

Both avoid raw per-frame full-map drawing and expose tile statistics. Choose explicitly for manual worlds; the Tiled world builder currently creates the sprite-pool implementation for its dynamic and auto modes.

When To Use

Use a dynamic tile layer when:

Tile-renderer selection matrix showing when the builder uses dynamic sprite pools or cached chunks and when a manual world may opt into a ring surfaceTile-renderer selection matrix showing when the builder uses dynamic sprite pools or cached chunks and when a manual world may opt into a ring surface

Good fitBetter alternative
The map scrolls and the visible tile pool is modest.Cached chunk tile when zoom-out or small tiles would require too many visible sprites.
Camera movement benefits from row/column repaint and a bounded surface.Ring surface, provided the visible ring fits the platform surface-size limit.
Tile contents are static but the camera moves frequently.Static tile when the whole map is small enough to cache into one surface.
You need per-tile compositor sprites in a foreground layer.Cached surface for non-tile custom terrain.

Hs2dWorldBuilder auto mode keeps dynamic rendering while the worst-case pool is at or under the configured threshold, then switches to cached chunks when the pool would be too large.

Construction Pattern

Manual worlds need a host sprite layer:

const tileHost = world.addHs2dSpriteLayer({
id: 'terrain-sprites',
width: screenWidth,
height: screenHeight,
clearColor: 0x00000000,
zoomScaleRatio: 0,
minScale: 1,
maxScale: 1,
offscreenX: -1000,
offscreenY: -1000,
});

const terrain = world.addTileLayer({
id: 'terrain',
mode: 'dynamic',
layer: tileHost,
tileWidth: 32,
tileHeight: 32,
worldColumns: level.width,
worldRows: level.height,
tileKinds,
regions: tileRegions,
minScale: 0.8,
poolPadding: 2,
zIndex: 0,
offscreenX: -1000,
offscreenY: -1000,
});

For a ring surface, no host sprite layer is required:

const terrain = world.addTileLayer({
id: 'terrain',
mode: 'dynamic',
renderMode: 'ring-surface',
tileWidth: 32,
tileHeight: 32,
worldColumns: level.width,
worldRows: level.height,
tileKinds,
regions: tileRegions,
minScale: 0.8,
poolPadding: 1,
clearColor: 0x0f172aff,
});

Tiled:

Tile layer: terrain
hs2d:mode = dynamic
hs2d:poolPadding = 2
hs2d:z = 20

hs2d:mode=auto can also resolve to the dynamic sprite-pool path. renderMode: 'ring-surface' is currently a manual Hs2dWorld.addTileLayer() option, not a Tiled property consumed by Hs2dWorldBuilder.

Update, Render, And Compose Behavior

PhaseBehavior
BuildAllocates tile sprites on the host layer compositor. The pool is sized from viewport, tile size, padding, and minScale.
World updateHs2dWorld.update calls tileLayer.update(camera) before render.
Tile updateComputes the first visible cell, active columns and rows, recycles entries whose world cell changed, swaps regions only when needed, parks empty cells, and moves drawable tiles.
Render/composeThe host sprite layer renders and composes the tile sprites with its other compositor sprites.

Stats from getStats() include pool size, visible tiles, recycled tiles, region changes, drawable changes, and moved tiles.

For the ring implementation, update shifts the retained logical ring and directly repaints entering rows and columns. Compose crops up to four wrapped regions from its surface. Ring stats report repaints, paints, clears, composed regions, and fallback strips.

preparationTileBudget is a deprecated, ignored compatibility option. The retained preparedTiles, preparedStripBlits, deadlinePreparedTiles, and preparationTileBudget stats are always zero, and preparationEnabled is always false. Do not build diagnostics or tuning controls around those fields.

Performance Notes

  • Pass the smallest supported camera scale as minScale so the renderer is sized before gameplay.
  • Keep tile size large enough that the visible pool remains within budget.
  • Use poolPadding sparingly; it prevents edge popping but increases pool and loop size.
  • Treat zoom below the declared minScale as unsupported. The sprite-pool path can cold-rebuild and grow; the ring path sizes its bounded surface from the declared minimum rather than the current smaller scale.
  • Use cached chunks for large sparse maps or very small tiles.
  • Keep tile data in arrays and regions; do not query raw Tiled JSON during frames.

Code Sample

protected override onUpdate(deltaMs: number, inputs: GameInput[]): void {
this.updatePlayer(deltaMs, inputs);
this.tickSceneCamera(deltaMs, this.mapZoom);
}

protected override updateSceneSprites(): void {
const stats = this.terrainLayer?.getStats();
this.visibleTiles = stats?.visibleTiles ?? 0;
}

The actual tile movement happens inside Hs2dTileLayer.update(camera), called by the world. Game code reads stats or changes camera state; it does not draw tiles.

Source References

SourceWhy it matters
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dTileLayer.tsDynamic tile pool sizing, active window, recycling, projection scale, stats, and hot path.
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dRingTileLayer.tsRing layout, entering-cell repaint, wrapped crop composition, tile mutation, and ring stats.
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dWorld.tsaddTileLayer dynamic mode and world tile update loop.
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dWorldBuilder.tsTiled hs2d:mode, auto-mode threshold, poolPadding, hs2d:spriteLayer, and z mapping.
../hosanna-ui-game-samples-public/src/hosanna-game-examples/diagnostics/H2dTilemapModeDemoController.tsCached chunks versus dynamic ring-surface diagnostic with camera, zoom, and repaint telemetry.
../hosanna-ui-game-samples-public/src/hosanna-game-examples/native-shoot-em-up/VerticalShooterLevelScene.tsExample map-driven terrain layer and stats usage in gameplay.
Talk to us