Jugable
Apuntarme
Fundamentos

Tilemaps con Tiled: diseñar niveles sin tocar código

Cómo montar niveles en Tiled y cargarlos en Phaser: capas, colisiones por propiedad, y objetos para spawns y triggers.

Poner el nivel en el código funciona hasta el segundo nivel. Después necesitas un editor, y el editor es Tiled — gratis, open source, y Phaser lo carga nativo.

La idea

Un tilemap es una cuadrícula de índices. En vez de guardar 3000 sprites, guardas 3000 números que apuntan a un tileset. Es compacto, rapidísimo de renderizar, y editable visualmente.

Estructura mínima

En Tiled, monta tres capas:

  • suelo — decorativa, sin colisión
  • paredes — con colisión
  • objetos — capa de objetos, no de tiles: spawns, puertas, enemigos

Separar la colisión en su propia capa es lo que te deja tener suelo bonito sin que cada baldosa decorativa bloquee al jugador.

Exporta como JSON, no como TMX. Phaser lee JSON.

Cargarlo

preload() {
  this.load.image('tiles', 'tileset.png')
  this.load.tilemapTiledJSON('nivel1', 'nivel1.json')
}

create() {
  const map = this.make.tilemap({ key: 'nivel1' })

  // "tileset-nombre-en-Tiled" tiene que coincidir EXACTO con el de Tiled
  const tileset = map.addTilesetImage('tileset-nombre-en-Tiled', 'tiles')

  map.createLayer('suelo', tileset, 0, 0)
  const paredes = map.createLayer('paredes', tileset, 0, 0)

  paredes.setCollisionByProperty({ colisiona: true })
  this.physics.add.collider(this.player, paredes)
}

El error número uno: el nombre del tileset en addTilesetImage debe coincidir literalmente con el que le pusiste en Tiled, no con el nombre del archivo. Si no coincide, no da error — simplemente no se dibuja nada, y te vuelves loco.

Colisión por propiedad

setCollisionByProperty({ colisiona: true }) requiere que en Tiled marques los tiles: selecciona el tileset, elige los tiles sólidos, y añádeles una propiedad booleana colisiona = true.

La alternativa es setCollisionByExclusion([-1]) (todo colisiona menos el vacío), más rápida de montar y mucho menos flexible. Empieza por propiedades; te lo agradecerás cuando quieras plataformas atravesables desde abajo.

La capa de objetos

Aquí es donde el tilemap deja de ser decorado y pasa a ser diseño de nivel:

const spawns = map.getObjectLayer('objetos').objects

spawns.forEach(obj => {
  if (obj.name === 'jugador') {
    this.player = this.physics.add.sprite(obj.x, obj.y, 'heroe')
  }
  if (obj.name === 'enemigo') {
    this.enemigos.create(obj.x, obj.y, 'slime')
  }
})

Ahora puedes colocar enemigos, checkpoints y puertas en el editor y el código no cambia. Ese es el objetivo: que diseñar un nivel no requiera programar.

Rendimiento

Los tilemaps grandes se dibujan enteros aunque no se vean. Si notas caídas:

paredes.setCullPadding(2)   // sólo dibuja lo cercano a la cámara

Y evita mapas de 500×500 en una sola capa; divide por zonas y cárgalas por separado.


Sesión 5 del curso presencial de Phaser.