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

При разработке игр часто возникает задача разместить на уровне множество однотипных интерактивных объектов, например, монеток или бонусов. Рисовать их вручную — долго и неудобно. В этой статье мы рассмотрим, как использовать редактор карт Tiled для удобного размещения объектов и как с помощью одного метода `createFromObjects` превратить их в полноценные спрайты Phaser, готовые к анимации и физике. Этот подход ускоряет создание контента и делает процесс более наглядным.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    controls;
    map;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/level-with-coin-objects.json');
        this.load.spritesheet('coin', 'assets/sprites/coin.png', { frameWidth: 32, frameHeight: 32 });
        this.load.image('ground_1x1', 'assets/tilemaps/tiles/ground_1x1.png');
    }

    create ()
    {
        this.map = this.add.tilemap('map');
        const tiles = this.map.addTilesetImage('ground_1x1');
        const layer = this.map.createLayer('Tile Layer', tiles);

        this.anims.create({
            key: 'spin',
            frames: this.anims.generateFrameNumbers('coin', { start: 0, end: 5 }),
            frameRate: 16,
            repeat: -1
        });

        // We convert all of the Tiled objects with an ID of 26 into sprites. They will get their width
        // & height from the Tiled tile object. Any custom properties on the tile object will also be
        // passed to the sprite creator (e.g. one of the tile object's has an alpha of 0.5).
        // var coins = map.createFromObjects('Coin Object Layer', 26, { key: 'coin' });

        const coins = this.map.createFromObjects('Coin Object Layer', { gid: 26, key: 'coin' });

        this.anims.play('spin', coins);

        const cursors = this.input.keyboard.createCursorKeys();

        const controlConfig = {
            camera: this.cameras.main,
            left: cursors.left,
            right: cursors.right,
            up: cursors.up,
            down: cursors.down,
            acceleration: 0.04,
            drag: 0.0005,
            maxSpeed: 0.7
        };

        this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
    }

    update (time, delta)
    {
        this.controls.update(delta);
    }
}

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

const game = new Phaser.Game(config);

Подготовка: карта и ресурсы

Всё начинается с загрузки ресурсов. В методе preload мы загружаем карту в формате JSON, созданную в Tiled, лист спрайтов для монетки и изображение тайла для земли. Обратите внимание на использование setBaseURL — это удобный способ задать базовый путь для всех последующих загрузок.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/level-with-coin-objects.json');
    this.load.spritesheet('coin', 'assets/sprites/coin.png', { frameWidth: 32, frameHeight: 32 });
    this.load.image('ground_1x1', 'assets/tilemaps/tiles/ground_1x1.png');
}

Создание слоёв и анимации

В методе create мы создаём саму карту (tilemap), добавляем к ней набор тайлов (tileset) и создаём слой для отрисовки. Это стандартный процесс для работы с тайловыми картами в Phaser.

Отдельно создаётся анимация spin для вращения монетки. Мы используем generateFrameNumbers, чтобы автоматически сгенерировать кадры анимации из листа спрайтов coin.

create ()
{
    this.map = this.add.tilemap('map');
    const tiles = this.map.addTilesetImage('ground_1x1');
    const layer = this.map.createLayer('Tile Layer', tiles);

    this.anims.create({
        key: 'spin',
        frames: this.anims.generateFrameNumbers('coin', { start: 0, end: 5 }),
        frameRate: 16,
        repeat: -1
    });

Волшебство метода `createFromObjects`

Ключевой момент — создание игровых объектов из данных Tiled. В редакторе карт мы создали специальный слой объектов (Coin Object Layer) и разместили на нём тайлы с монетками (у этих тайлов в наборе GID = 26).

Метод this.map.createFromObjects находит все объекты на указанном слое с заданным GID и создаёт из них спрайты Phaser. В параметрах мы указываем, какой ключ текстуры (key: 'coin') использовать для этих спрайтов. Метод автоматически унаследует от объектов Tiled их координаты, размеры и любые пользовательские свойства (например, прозрачность).

После создания массива спрайтов coins, мы легко запускаем на них одну и ту же анимацию spin.

const coins = this.map.createFromObjects('Coin Object Layer', { gid: 26, key: 'coin' });
this.anims.play('spin', coins);

Управление камерой для навигации

Чтобы можно было осмотреть всю карту, в примере реализовано плавное управление камерой с клавиатуры. Создаётся экземпляр SmoothedKeyControl, который обрабатывает нажатия стрелок и двигает камеру с заданным ускорением и максимальной скоростью. Обновление состояния управления происходит в методе update.

const cursors = this.input.keyboard.createCursorKeys();
const controlConfig = {
    camera: this.cameras.main,
    left: cursors.left,
    right: cursors.right,
    up: cursors.up,
    down: cursors.down,
    acceleration: 0.04,
    drag: 0.0005,
    maxSpeed: 0.7
};
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
update (time, delta)
{
    this.controls.update(delta);
}

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

Метод createFromObjects — это мощный мост между дизайном уровней в Tiled и логикой игры в Phaser. Он позволяет дизайнеру легко расставлять объекты, а разработчику — мгновенно получать их в коде как готовые сущности. Для экспериментов попробуйте: 1. Добавить пользовательские свойства объектам в Tiled (например, points: 100) и читать их в коде спрайта. 2. Использовать разные GID для создания различных типов объектов (монетки, ловушки, ключи) за один вызов метода. 3. Настроить физические тела для созданных спрайтов, чтобы они могли сталкиваться с игроком.