О чем этот пример

При импорте карт из Tiled в Phaser разработчики иногда сталкиваются с проблемой: тайловый слой, созданный с использованием коллекции изображений (Image Collection), не отображается. Это происходит потому, что Phaser по умолчанию не загружает такие коллекции как единый тайлсет. В этой статье мы разберем пример кода, который решает эту проблему, и наглядно покажем, как вручную создать тайлсеты из каждого изображения в коллекции, чтобы ваш слой наконец появился на экране. Этот подход полезен для работы с картами, где объекты представлены уникальными спрайтами, а не повторяющимися тайлами.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    controls;

    preload ()
    {
        
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('tiles', 'assets/tilemaps/tiles/dangerous-kiss-x2.png');
        this.load.tilemapTiledJSON('map2', 'assets/tilemaps/maps/dangerous-kiss.json');

        this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/image-collection.json');
        this.load.image('advanced_wars_tank', 'assets/sprites/advanced_wars_tank.png');
        this.load.image('alienbusters', 'assets/sprites/alienbusters.png');
        this.load.image('aqua_ball', 'assets/sprites/aqua_ball.png');
        this.load.image('arrow', 'assets/sprites/arrow.png');
        this.load.image('car90', 'assets/sprites/car90.png');
        this.load.image('car-yellow', 'assets/sprites/car-yellow.png');
        this.load.image('columns-red', 'assets/sprites/columns-red.png');
    }

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

        // The map was created with 8x8 tiles, but we want to load it with a 2x high resolution tileset
        map.setBaseTileSize(8, 8);

        const tilesetCollections = [];

        map.imageCollections.forEach((collection) =>
        {
            collection.images.forEach((img) =>
            {
                let tmpname = img.image;
                tmpname = tmpname.replace('../../sprites/', '').replace('.png', '');

                const tileset = map.addTilesetImage(img.image, tmpname);
                tilesetCollections.push(tileset);
            });
        })

        const layer = map.createLayer('MapLayer', tilesetCollections, 0, 0);

        const debugWindow = this.add.graphics();
        map.renderDebug(debugWindow);
    }
}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    backgroundColor: '#2d2d88',
    scene: Example
};

const game = new Phaser.Game(config);

Суть проблемы: почему слой не рисуется?

В редакторе Tiled можно создать тайлсет типа "Коллекция изображений" (Image Collection). В отличие от обычного tileset (набора тайлов), это просто список отдельных изображений-спрайтов. При экспорте в JSON эта коллекция сохраняется в массиве map.imageCollections.

Проблема в том, что Phaser при загрузке карты с помощью load.tilemapTiledJSON автоматически создает тайлсеты только для обычных наборов (tileset). Коллекции изображений он игнорирует. Поэтому, когда вы пытаетесь создать слой (createLayer), передавая ему только что загруженную карту, система не находит нужных тайлсетов и слой остается пустым.

Исходный код примера как раз демонстрирует решение: мы должны вручную обойти коллекцию и для каждого изображения создать отдельный тайлсет.

Анализ кода: загрузка и подготовка

Давайте посмотрим на ключевые части метода preload и начала create. Здесь загружается сама карта и отдельные изображения, которые в ней используются.

this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/image-collection.json');
this.load.image('advanced_wars_tank', 'assets/sprites/advanced_wars_tank.png');
this.load.image('alienbusters', 'assets/sprites/alienbusters.png');
// ... загрузка остальных изображений

Важный момент: каждое изображение из коллекции должно быть загружено отдельно через load.image. Ключ загрузки (например, 'advanced_wars_tank') мы будем использовать позже. В данном примере имена файлов совпадают с именами, которые получаются после обработки пути в коде (см. ниже).

Сердце решения: создание тайлсетов из коллекции

Основная логика находится в методе create. Мы получаем объект карты и вручную создаем из коллекции массив тайлсетов.

const map = this.make.tilemap({ key: 'map' });
map.setBaseTileSize(8, 8);

const tilesetCollections = [];

map.imageCollections.forEach((collection) =>
{
    collection.images.forEach((img) =>
    {
        let tmpname = img.image;
        tmpname = tmpname.replace('../../sprites/', '').replace('.png', '');

        const tileset = map.addTilesetImage(img.image, tmpname);
        tilesetCollections.push(tileset);
    });
})

1. map.imageCollections.forEach — перебираем все коллекции изображений в карте (обычно одна). 2. collection.images.forEach — внутри коллекции перебираем каждое изображение. 3. Строка с tmpname — чистит путь к файлу, оставляя только имя. Это нужно, потому что в JSON из Tiled путь может быть относительным (например, ../../sprites/advanced_wars_tank.png), а в Phaser мы загружали изображение по короткому имени (advanced_wars_tank). 4. map.addTilesetImage(img.image, tmpname) — ключевой вызов. Он создает тайлсет, связывая исходный путь из Tiled (img.image) с ключом загруженной текстуры (tmpname). Полученный тайлсет добавляется в массив tilesetCollections.

Создание слоя и отладка

После того как массив тайлсетов готов, мы можем создать слой и включить отладочную отрисовку.

const layer = map.createLayer('MapLayer', tilesetCollections, 0, 0);

const debugWindow = this.add.graphics();
map.renderDebug(debugWindow);

1. map.createLayer('MapLayer', tilesetCollections, 0, 0) — создает слой с именем 'MapLayer' (имя должно совпадать с именем слоя в Tiled), используя наш массив tilesetCollections. Теперь у движка есть все необходимые данные для отрисовки. 2. map.renderDebug(debugWindow) — рисует поверх карты отладочную сетку, которая показывает границы тайлов (в данном случае — границы каждого спрайта). Это помогает визуально убедиться, что слой создан корректно, даже если сами спрайты не загрузились.

Что попробовать дальше

Таким образом, для корректной работы с Image Collection из Tiled в Phaser необходим дополнительный шаг: обход map.imageCollections и ручное создание тайлсетов через addTilesetImage. Это надежный паттерн, решающий проблему "пустого" слоя. Для экспериментов попробуйте: изменить логику обработки имени файла (tmpname), если ваши пути другие; создать несколько слоев с разными коллекциями; или использовать полученный массив тайлсетов для создания статических спрайтовых групп, что может быть полезно для оптимизации.