Loading...
Loading...
This skill should be used when the user asks to "add tilemap", "create a level", "Tiled editor", "tile collision", "object layer", "create map", "tilemap not showing", "level design", "tile layer", "game map", "spawn point", "trigger zone", or "map collision".
npx skill4agent add yakoub-ai/phaser4-gamedev phaser-tilemapmap.addTilesetImage()collidesbooltruelayer.setCollisionByProperty({ collides: true })| Layer Name | Type | Purpose |
|---|---|---|
| Tile Layer | Sky, distant scenery — no collision |
| Tile Layer | Main walkable surface — collision enabled |
| Tile Layer | Spikes, lava — overlap (not collide) |
| Tile Layer | Trees, arches that render in front of player |
| Object Layer | Spawn points, triggers, enemies |
ObjectsPlayerSpawnEnemyhealthpatrol.json.tsxpublic/assets/tilemaps/level1.jsonpublic/assets/images/terrain.pngpreload(): void {
// Key must match first arg of map.addTilesetImage()
this.load.tilemapTiledJSON('level1', 'assets/tilemaps/level1.json');
// Key must match second arg of map.addTilesetImage()
this.load.image('terrain', 'assets/images/terrain.png');
}private map!: Phaser.Tilemaps.Tilemap;
private groundLayer!: Phaser.Tilemaps.TilemapLayer;
create(): void {
this.map = this.make.tilemap({ key: 'level1' });
// First arg: tileset name as set in Tiled (must match exactly, case-sensitive)
// Second arg: the this.load.image() key
const tileset = this.map.addTilesetImage('terrain', 'terrain');
// Decorative background — no collision
const bgLayer = this.map.createLayer('Background', tileset!, 0, 0);
// Main ground layer — collision enabled below
this.groundLayer = this.map.createLayer('Ground', tileset!, 0, 0)!;
// Foreground renders above the player
const fgLayer = this.map.createLayer('Foreground', tileset!, 0, 0);
fgLayer!.setDepth(10); // player depth should be 1–9
}addTilesetImage"name""tilesets"collides: truethis.groundLayer.setCollisionByProperty({ collides: true });
this.physics.add.collider(this.player, this.groundLayer);this.groundLayer.setCollisionBetween(1, 10);this.groundLayer.setCollisionByExclusion([-1, 0]);const debugGraphics = this.add.graphics();
this.groundLayer.renderDebug(debugGraphics, {
tileColor: null, // non-colliding tiles
collidingTileColor: new Phaser.Display.Color(243, 134, 48, 128), // orange
faceColor: new Phaser.Display.Color(40, 39, 37, 255), // face outlines
});create(): void {
const tileset = this.map.addTilesetImage('terrain', 'terrain')!;
const bgLayer = this.map.createLayer('Background', tileset, 0, 0); // depth 0 (default)
const groundLayer = this.map.createLayer('Ground', tileset, 0, 0)!; // depth 0 (default)
const fgLayer = this.map.createLayer('Foreground', tileset, 0, 0);
fgLayer!.setDepth(10); // renders above player
groundLayer.setCollisionByProperty({ collides: true });
// Player and enemies should have depth between 1 and 9
this.player.setDepth(5);
}create(): void {
// Find a single named object — returns the first match
const spawnPoint = this.map.findObject('Objects', obj => obj.name === 'PlayerSpawn');
this.player = this.physics.add.sprite(spawnPoint!.x!, spawnPoint!.y!, 'player');
// Get all objects of a given type (Tiled "Class" field in 1.8+)
const enemyObjects = this.map.filterObjects('Objects', obj => obj.type === 'Enemy');
enemyObjects?.forEach(obj => {
// Access custom properties as an array: obj.properties
const props = this.parseProperties(obj.properties);
this.spawnEnemy(obj.x!, obj.y!, props.health ?? 100);
});
// Find trigger zones (rectangles placed in Tiled)
const triggers = this.map.filterObjects('Objects', obj => obj.type === 'Trigger');
triggers?.forEach(obj => {
const zone = this.add.zone(obj.x! + obj.width! / 2, obj.y! + obj.height! / 2, obj.width!, obj.height!);
this.physics.world.enable(zone);
this.physics.add.overlap(this.player, zone, () => {
console.log(`Entered trigger: ${obj.name}`);
});
});
}
// Helper: convert Tiled properties array to plain object
private parseProperties(props?: { name: string; value: unknown }[]): Record<string, unknown> {
if (!props) return {};
return Object.fromEntries(props.map(p => [p.name, p.value]));
}create(): void {
// Constrain physics bodies
this.physics.world.setBounds(0, 0, this.map.widthInPixels, this.map.heightInPixels);
// Constrain camera
this.cameras.main.setBounds(0, 0, this.map.widthInPixels, this.map.heightInPixels);
// Follow the player
this.cameras.main.startFollow(this.player, true, 0.1, 0.1); // lerp x/y = 0.1 for smooth follow
}// Read a tile at a world position
const tile = this.groundLayer.getTileAtWorldXY(ptr.worldX, ptr.worldY);
if (tile) {
console.log(`Tile index: ${tile.index}`);
}
// Place a tile at a world position
this.groundLayer.putTileAtWorldXY(5, ptr.worldX, ptr.worldY);
// Remove a tile (sets index to -1 / empty)
this.groundLayer.removeTileAtWorldXY(ptr.worldX, ptr.worldY);
// Convert between world and tile coordinates
const tileXY = this.groundLayer.worldToTileXY(worldX, worldY)!;
const worldXY = this.groundLayer.tileToWorldXY(tileXY.x, tileXY.y)!;create(): void {
const cloudLayer = this.map.createLayer('Clouds', tileset!, 0, 0);
cloudLayer!.setScrollFactor(0.2); // moves at 20% camera speed (furthest back)
const hillLayer = this.map.createLayer('Hills', tileset!, 0, 0);
hillLayer!.setScrollFactor(0.5); // moves at 50% camera speed
// Ground layer
const groundLayer = this.map.createLayer('Ground', tileset!, 0, 0);
groundLayer!.setScrollFactor(1); // moves at 100% (default)
}create(): void {
const tiles1 = this.map.addTilesetImage('tileset-a', 'tiles-a');
const tiles2 = this.map.addTilesetImage('tileset-b', 'tiles-b');
// Pass an array of tilesets — Phaser resolves tile GIDs automatically
const layer = this.map.createLayer('Ground', [tiles1!, tiles2!], 0, 0);
}references/tilemap-api.mdreferences/tiled-workflow.md