~1 мин чтения

Тайлмапы

Загрузка карт Tiled в формате JSON, отрисовка слоёв, построение столкновений и изменение тайлов во время выполнения.

Система тайлмапов Phaser 4 читает JSON-экспорты из Tiled. Вы создаёте карту во внешнем редакторе, а Phaser берёт на себя отрисовку, столкновения и изменение во время выполнения.

Загрузка карты Tiled

Задействованы два файла: JSON карты и изображение тайлсета, на которое она ссылается.

preload() {
	this.load.tilemapTiledJSON('level-1', 'assets/maps/level-1.json');
	this.load.image('tiles', 'assets/tilesets/world.png');
}

Затем собираем карту и её слои:

create() {
	const map = this.make.tilemap({ key: 'level-1' });

	// Первый аргумент — имя тайлсета *как оно задано в Tiled*; второй —
	// ключ кэша загруженного изображения. Часто они совпадают.
	const tileset = map.addTilesetImage('world', 'tiles');

	const ground = map.createLayer('Ground', tileset, 0, 0);
	const walls = map.createLayer('Walls', tileset, 0, 0);
	const decor = map.createLayer('Decor', tileset, 0, 0);
}

Третий и четвёртый аргументы — мировые координаты левого верхнего угла слоя, обычно 0, 0.

Столкновения

Три идиоматичных способа отметить, какие тайлы участвуют в столкновениях:

// 1. По индексу тайла — точные ID тайлов из тайлсета.
walls.setCollision([12, 13, 25, 26]);

// 2. По диапазону.
walls.setCollisionBetween(1, 99);

// 3. По пользовательскому свойству Tiled — лучший вариант, если вы управляете источником.
walls.setCollisionByProperty({ collides: true });

Затем добавляем коллайдер:

this.physics.add.collider(player, walls);

Для односторонних платформ, склонов и других столкновений с учётом угла см. справочник API Tilemaps — методы TilemapLayer.setCollision* и специфичный для Arcade TileCollisionGroup покрывают большинство случаев.

Слои объектов

Слои объектов Tiled — это канонический способ размещать игровые маркеры: точку появления игрока, позиции врагов, двери. Они приходят в виде списка обычных объектов:

const spawns = map.getObjectLayer('Spawns');
spawns?.objects.forEach((obj) => {
	switch (obj.name) {
		case 'player': this.player = this.physics.add.sprite(obj.x!, obj.y!, 'player'); break;
		case 'enemy':  this.enemies.create(obj.x!, obj.y!, 'enemy'); break;
	}
});

Свойство properties каждого объекта несёт пользовательские свойства Tiled в виде записей {name, type, value} — если вы часто с ними работаете, упростите доступ небольшим хелпером, разворачивающим их в плоский объект.

Границы камеры

Почти всегда привязывайте карту к камере:

this.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.physics.world.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.cameras.main.startFollow(this.player);

Без этого камера будет спокойно прокручиваться за края карты в пустоту.

Изменение во время выполнения

Слои предоставляют прямой доступ к тайлам:

const tile = walls.getTileAtWorldXY(player.x, player.y - 32);
if (tile?.properties.breakable) {
	walls.removeTileAt(tile.x, tile.y);
}

// Заменяем каждый экземпляр одного тайла на другой.
ground.swapByIndex(45, 67);

// Прорезаем линию из тайлов.
for (let x = 0; x < 8; x++) walls.putTileAt(12, startX + x, startY);

Обратите внимание: координаты тайлов здесь — это тайловое пространство (0, 0 — это левый верхний тайл), а не пиксели. Используйте варианты WorldXY, когда у вас пиксельные координаты, и индексные варианты, когда у вас тайловые координаты.

Производительность

  • Статические слои дёшевы. Phaser батчирует слой в один или несколько вызовов отрисовки. Карта 200×200 отрисовывается практически бесплатно.
  • Свойства по тайлам стоят времени поиска. Если вы обнаружили, что перебираете каждый тайл каждый кадр, кэшируйте результат.
  • Отсечение (culling) включено по умолчанию. Тайлы за пределами камеры пропускаются. Не отключайте отсечение, не проведя сначала замеры.

Связанное