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

В разработке игр часто требуется выходить за рамки статичного уровня. Этот пример демонстрирует, как динамически управлять размером слоя тайловой карты и настраивать камеру для плавного скроллинга. Вы научитесь программно изменять геометрию игрового мира и привязывать к ней виртуальную камеру, что является основой для создания расширяемых уровней или эффектов "растягивания" мира.

Версия 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.tilemapTiledJSON('map', 'assets/tilemaps/maps/super-mario.json');
        this.load.image('tiles', 'assets/tilemaps/tiles/super-mario.png');
    }

    create ()
    {
        const map = this.make.tilemap({ key: 'map' });
        const tileset = map.addTilesetImage('SuperMarioBros-World1-1', 'tiles');
        const layer = map.createLayer('World1', tileset, 0, 0);

        layer.width = 200;

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

        const controlConfig = {
            camera: this.cameras.main,
            left: cursors.left,
            right: cursors.right,
            speed: 0.5
        };

        this.controls = new Phaser.Cameras.Controls.FixedKeyControl(controlConfig);
        this.cameras.main.setBounds(0, 0, layer.x + layer.width, 0);
    }

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

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

const game = new Phaser.Game(config);

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

В методе preload() загружаются два ключевых ресурса: JSON-файл с данными карты, созданный в Tiled Editor, и изображение с тайлами.

this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/super-mario.json');
this.load.image('tiles', 'assets/tilemaps/tiles/super-mario.png');

В create() происходит инициализация игрового мира. Сначала создается объект тайловой карты (Tilemap), затем он связывается с загруженным изображением тайлов через addTilesetImage. После этого создается визуальный слой (TilemapLayer), который и отрисовывается на сцене.

const map = this.make.tilemap({ key: 'map' });
const tileset = map.addTilesetImage('SuperMarioBros-World1-1', 'tiles');
const layer = map.createLayer('World1', tileset, 0, 0);

Динамическое изменение ширины слоя

Самая важная строка в этом примере — модификация свойства width созданного слоя. Исходная ширина слоя определяется загруженной картой. Присвоив новое значение, мы искусственно "растягиваем" игровой мир. Физические границы и отрисовка тайлов при этом не меняются, но изменяется пространство, по которому может перемещаться камера.

layer.width = 200;

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

Настройка управления камерой

Для перемещения по увеличенному миру нужен инструмент управления камерой. Сначала создается объект для отслеживания нажатий стрелок клавиатуры.

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

Затем создается конфигурация для встроенного контроллера камеры FixedKeyControl. В конфиге указывается, какая камера управляется (this.cameras.main), какие клавиши отвечают за движение влево и вправо, и скорость перемещения.

const controlConfig = {
    camera: this.cameras.main,
    left: cursors.left,
    right: cursors.right,
    speed: 0.5
};
this.controls = new Phaser.Cameras.Controls.FixedKeyControl(controlConfig);

Установка границ для камеры

Чтобы камера не выходила за пределы игрового мира, необходимо задать её границы с помощью метода setBounds. Ключевой момент расчета: правая граница (width) устанавливается как сумма начальной позиции слоя по X (layer.x) и его новой, увеличенной ширины (layer.width).

this.cameras.main.setBounds(0, 0, layer.x + layer.width, 0);

Поскольку в этом примере скроллинг только горизонтальный, границы по высоте (Y и height) установлены в 0. Камера будет плавно перемещаться в рамках рассчитанной ширины, позволяя игроку осмотреть весь "растянутый" слой.

Обновление состояния контроллера

Логика перемещения камеры, заложенная в контроллере FixedKeyControl, должна обновляться каждый кадр. Для этого в методе update() вызывается его метод update, куда передается delta-время — разница в миллисекундах с предыдущим кадром. Это обеспечивает плавное и независимое от частоты кадров движение.

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

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

Пример показывает простой, но эффективный способ трансформации игрового пространства через изменение свойств слоя тайловой карты и корректную настройку камеры. Для экспериментов попробуйте изменить layer.width в зависимости от действий игрока, реализовать вертикальный скроллинг, добавив управление по оси Y, или анимировать изменение ширины для создания эффекта "разворачивающегося" уровня.