Tilemaps
Tilemaps allow you to render large tile-based environments efficiently using chunked rendering. The TileMap2D class handles maps with multiple layers and automatic chunking for performance.
Creating a Tilemap
import { TileMap2D, TiledLoader } from 'three-flatland';
// Load a Tiled mapconst mapData = await TiledLoader.load('/maps/level1.json');
// Create tilemapconst tilemap = new TileMap2D({ data: mapData, chunkSize: 16, // Tiles per chunk (default: 16)});
scene.add(tilemap);
// In your animation loopfunction animate() { tilemap.update(deltaMs); // Update animated tiles renderer.render(scene, camera);}import { Suspense, useRef } from 'react';import { extend, useFrame, useLoader } from '@react-three/fiber/webgpu';import { TileMap2D, TiledLoader } from 'three-flatland/react';
extend({ TileMap2D });
function Level() { // useLoader suspends while loading, presets applied automatically const mapData = useLoader(TiledLoader, '/maps/level1.json'); const tilemapRef = useRef<TileMap2D>(null);
useFrame((_, delta) => { tilemapRef.current?.update(delta * 1000); });
return <tileMap2D ref={tilemapRef} data={mapData} chunkSize={16} />;}
// Wrap with Suspense in parentfunction App() { return ( <Suspense fallback={null}> <Level /> </Suspense> );}Map Data Structure
The TileMapData interface describes the map format:
interface TileMapData { width: number; // Map width in tiles height: number; // Map height in tiles tileWidth: number; // Tile width in pixels tileHeight: number; // Tile height in pixels orientation: 'orthogonal' | 'isometric' | 'staggered' | 'hexagonal'; renderOrder: 'right-down' | 'right-up' | 'left-down' | 'left-up'; infinite: boolean; // Infinite map flag tilesets: TilesetData[]; tileLayers: TileLayerData[]; objectLayers: ObjectLayerData[]; properties?: Record<string, unknown>;}Loading Maps
Tiled Editor (.json/.tmj)
Load maps created with the Tiled Map Editor:
import { TileMap2D, TiledLoader } from 'three-flatland';
const mapData = await TiledLoader.load('/maps/level1.json');const tilemap = new TileMap2D({ data: mapData });scene.add(tilemap);import { Suspense } from 'react';import { extend, useLoader } from '@react-three/fiber/webgpu';import { TileMap2D, TiledLoader } from 'three-flatland/react';
extend({ TileMap2D });
function TiledMap() { const mapData = useLoader(TiledLoader, '/maps/level1.json'); return <tileMap2D data={mapData} />;}
// Usage with Suspense<Suspense fallback={<LoadingIndicator />}> <TiledMap /></Suspense>LDtk Editor (.ldtk)
Load maps created with LDtk:
import { TileMap2D, LDtkLoader } from 'three-flatland';
// Load a specific level by nameconst mapData = await LDtkLoader.load('/maps/world.ldtk', 'Level_0');const tilemap = new TileMap2D({ data: mapData });scene.add(tilemap);
// List all levels in projectconst levelIds = await LDtkLoader.getLevelIds('/maps/world.ldtk');import { Suspense } from 'react';import { extend, useLoader } from '@react-three/fiber/webgpu';import { TileMap2D, LDtkLoader } from 'three-flatland/react';
extend({ TileMap2D });
function LDtkMap() { // Loads first level by default const mapData = useLoader(LDtkLoader, '/maps/world.ldtk'); return <tileMap2D data={mapData} />;}
// Specify level via extension callbackfunction SpecificLevel() { const mapData = useLoader(LDtkLoader, '/maps/world.ldtk', (loader) => { loader.levelId = 'Level_0'; }); return <tileMap2D data={mapData} />;}
// Usage with Suspense<Suspense fallback={<LoadingIndicator />}> <LDtkMap /></Suspense>Working with Layers
Access and manipulate tile layers:
// Get layer by indexconst groundLayer = tilemap.getLayerAt(0);const wallsLayer = tilemap.getLayerAt(1);
// Toggle visibilitygroundLayer.visible = false;
// Get layer countconsole.log(tilemap.layerCount);Tile Operations
Read and modify tiles at runtime:
// Get layerconst layer = tilemap.getLayerAt(0);
// Read tile at positionconst tileGid = layer.getTileAt(x, y);
// Set tile at positionlayer.setTileAt(x, y, newTileGid);Coordinate Conversion
Convert between world and tile coordinates:
// World position to tile positionconst tilePos = tilemap.worldToTile(worldX, worldY);console.log(tilePos.x, tilePos.y);
// Tile position to world positionconst worldPos = tilemap.tileToWorld(tileX, tileY);console.log(worldPos.x, worldPos.y);Statistics
Get information about the tilemap:
console.log('Total tiles:', tilemap.totalTileCount);console.log('Total chunks:', tilemap.totalChunkCount);console.log('Layer count:', tilemap.layerCount);Chunked Rendering
Tilemaps use chunked rendering for performance. Configure the chunk size based on your needs:
const tilemap = new TileMap2D({ data: mapData, chunkSize: 16, // 16x16 tiles per chunk (default)});Smaller chunks = more draw calls but finer culling. Larger chunks = fewer draw calls but coarser culling. The default of 16 works well for most cases.
Animated Tiles
Tiles with animation data (from Tiled) are automatically animated. Call update() each frame:
function animate() { const deltaMs = /* time since last frame */; tilemap.update(deltaMs); renderer.render(scene, camera);}Disposing
Clean up resources when done:
scene.remove(tilemap);tilemap.dispose();