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

Часто в 2D-играх на тайловых картах нужно, чтобы некоторые элементы вели себя как независимые объекты — например, монеты, которые можно собрать, или препятствия, которые можно разрушить. Вручную создавать спрайты для каждого такого тайла — долго и неэффективно. В этой статье мы разберем метод `createFromTiles`, который позволяет автоматически конвертировать выбранные тайлы карты в полноценные спрайты Phaser. Это мощный инструмент для быстрого прототипирования и создания динамических игровых миров, где часть окружения должна реагировать на действия игрока.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.spritesheet('fantasy-tiles', 'assets/tilemaps/tiles/fantasy-tiles.png', { frameWidth: 64 });
        this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/create from tiles.json');
    }

    create ()
    {
        const map = this.add.tilemap('map');

        const tiles = map.addTilesetImage('fantasy-tiles');

        const layer = map.createLayer('Tile Layer 1', tiles);

        //  Because we have loaded the tileset as a spritesheet, we can set the
        //  'useSpriteSheet' property to set the texture and frame when creating the sprites.
        //  If you have loaded the tileset as an image, then this will not work and you
        //  should set the texture and frame manually after creating the sprites.

        const sprites = map.createFromTiles([ 53, 50 ], -1, { useSpriteSheet: true });

        //  Bounce the sprites just to show they're no longer tiles:

        this.tweens.add({
            targets: sprites,
            y: '-=32',
            duration: 1000,
            ease: 'Sine.easeInOut',
            yoyo: true,
            repeat: -1
        });
    }
}

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

const game = new Phaser.Game(config);

Загрузка ресурсов и подготовка карты

Как и в большинстве примеров с Phaser, работа начинается с метода preload, где мы загружаем необходимые ассеты. Ключевой момент — тайлсет загружается не как обычное изображение, а как спрайтшит (spritesheet). Это означает, что мы указываем размер одного кадра (тайла), и Phaser автоматически разбивает изображение на отдельные фреймы.

this.load.spritesheet('fantasy-tiles', 'assets/tilemaps/tiles/fantasy-tiles.png', { frameWidth: 64 });
this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/create from tiles.json');

Также загружается сама тайловая карта в формате JSON, экспортированная из редактора Tiled. В методе create мы создаем объект карты, добавляем к ней загруженный тайлсет и создаем слой для отрисовки.

const map = this.add.tilemap('map');
const tiles = map.addTilesetImage('fantasy-tiles');
const layer = map.createLayer('Tile Layer 1', tiles);

На этом этапе карта отображается как статичное изображение. Все ее элементы — это просто части одного большого холста (тайлового слоя).

Волшебный метод createFromTiles

Сердце примера — вызов метода map.createFromTiles. Его задача — найти на карте все тайлы с указанными индексами, удалить их с тайлового слоя и создать на их месте отдельные игровые объекты (спрайты).

const sprites = map.createFromTiles([ 53, 50 ], -1, { useSpriteSheet: true });

Давайте разберем аргументы: 1. **Первый аргумент ([53, 50])** — это массив индексов тайлов, которые мы хотим преобразовать. Индекс соответствует номеру тайла в тайлсете. В нашем случае мы ищем тайлы с номерами 53 и 50. 2. **Второй аргумент (-1)** — это индекс, на который нужно заменить оригинальный тайл *на карте* после создания спрайта. Значение -1 означает "удалить тайл", то есть оставить на его месте пустоту. Если бы мы указали, например, `0`, то на месте каждого найденного тайла появился бы тайл с индексом 0. 3. **Третий аргумент ({ useSpriteSheet: true })** — это ключевая опция. Она говорит Phaser: "Мы загрузили тайлсет как спрайтшит, поэтому для каждого созданного спрайта автоматически задай текстуру и кадр (frame), соответствующие оригинальному тайлу". Без этой опции спрайты создались бы с пустой текстурой.

Работа со спрайтами после создания

Метод createFromTiles возвращает массив созданных спрайтов. Это обычные объекты Phaser.GameObjects.Sprite, и с ними можно делать все, что угодно: добавлять физические тела, анимации, обрабатывать столкновения.

В примере мы добавляем к ним простую анимацию-покачивание с помощью системы твинов, чтобы наглядно показать, что теперь это — независимые, живые объекты.

this.tweens.add({
    targets: sprites,
    y: '-=32',
    duration: 1000,
    ease: 'Sine.easeInOut',
    yoyo: true,
    repeat: -1
});

Этот твин перемещает все спрайты в массиве sprites на 32 пикселя вверх за 1 секунду, а затем возвращает обратно, создавая эффект плавающего движения. Параметр yoyo: true отвечает за возврат к начальному состоянию, а repeat: -1 делает анимацию бесконечной.

Важные нюансы и ограничения

1. **Спрайтшит против Изображения:** Опция useSpriteSheet: true работает *только* если тайлсет был загружен через load.spritesheet. Если вы загрузили его как обычное изображение (load.image), фреймы не будут созданы, и опция не сработает. В таком случае вам придется вручную задавать текстуру и фрейм для созданных спрайтов в цикле. 2. **Слои и индексы:** Метод работает с конкретным слоем карты. В примере он неявно использует слой, созданный ранее. Индексы тайлов (53, 50) — это их глобальные ID в тайлсете, которые можно увидеть в редакторе Tiled. 3. **Производительность:** Преобразование сотен тайлов в спрайты может негативно сказаться на производительности, так как каждый спрайт — это отдельный объект для отрисовки. Используйте этот метод для относительно небольшого количества динамических объектов.

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

Метод createFromTiles — это элегантный мост между статичным миром тайловых карт и динамичным миром игровых объектов Phaser. Он идеально подходит для быстрого создания собираемых предметов, разрушаемых блоков или точек появления врагов прямо на основе данных из Tiled. Для экспериментов попробуйте: преобразовать тайлы в спрайты с физическим телом (this.physics.add.sprite), чтобы они падали; использовать разные индексы для замены (вместо -1) и создать эффект "сбора" с анимацией исчезновения; или привязать к созданным спрайтам пользовательские данные (custom properties из Tiled) для управления их поведением.