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

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

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

Живой запуск

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

Исходный код


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

var controls;

var game = new Phaser.Game(config);

function preload ()
{
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.tilemapTiledJSON('map1', 'assets/tilemaps/maps/super-mario.json');
    this.load.image('tiles1', 'assets/tilemaps/tiles/super-mario.png');

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

function create ()
{
    var map1 = this.make.tilemap({ key: 'map1' });
    var tileset1 = map1.addTilesetImage('SuperMarioBros-World1-1', 'tiles1');
    var layer1 = map1.createLayer('World1', tileset1, 0, 0);

    var map2 = this.add.tilemap('map3');
    var tileset2 = map2.addTilesetImage('SuperMarioBrosMap1-3_bank.png', 'tiles3');
    var layer2 = map2.createLayer('ShoeBox Tile Grab', tileset2, 700, 300);

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

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

    controls = new Phaser.Cameras.Controls.FixedKeyControl(controlConfig);

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

    this.input.once('pointerdown', () => {

        map1.destroyLayer(layer1);

    });
}

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

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

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

function preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.tilemapTiledJSON('map1', 'assets/tilemaps/maps/super-mario.json');
    this.load.image('tiles1', 'assets/tilemaps/tiles/super-mario.png');

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

Создание тайловых слоев и управление камерой

В функции create создаются два слоя из разных карт. Первый слой (layer1) создается через фабрику this.make.tilemap, а второй (layer2) — через менеджер this.add.tilemap. Оба подхода валидны и приводят к созданию игровых объектов. Для управления камерой с клавиатуры используется Phaser.Cameras.Controls.FixedKeyControl. Важный момент: границы камеры (setBounds) устанавливаются с учетом ширины второго слоя и дополнительного отступа, что позволяет камере прокручивать всю сцену.

function create ()
{
    var map1 = this.make.tilemap({ key: 'map1' });
    var tileset1 = map1.addTilesetImage('SuperMarioBros-World1-1', 'tiles1');
    var layer1 = map1.createLayer('World1', tileset1, 0, 0);

    var map2 = this.add.tilemap('map3');
    var tileset2 = map2.addTilesetImage('SuperMarioBrosMap1-3_bank.png', 'tiles3');
    var layer2 = map2.createLayer('ShoeBox Tile Grab', tileset2, 700, 300);

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

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

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

Динамическое уничтожение слоя по событию

Ключевая логика примера — удаление первого слоя (layer1) по клику мыши. Для этого используется метод map1.destroyLayer(layer1), который вызывается внутри обработчика события pointerdown. Этот метод полностью уничтожает слой: удаляет все тайлы, освобождает связанную с ним память и убирает его из списка отрисовки. После вызова destroyLayer слой больше не будет отображаться на экране, и все ссылки на него станут недействительными.

this.input.once('pointerdown', () => {
    map1.destroyLayer(layer1);
});

Важно отметить, что метод вызывается у объекта тайловой карты (map1), а не у самого слоя или сцены. Это обеспечивает корректное внутреннее обновление состояния карты.

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

Функция update отвечает за обновление управления камерой с помощью FixedKeyControl. Даже после уничтожения слоя управление камерой продолжает работать, так как контроллер не зависит от уничтоженных объектов. Однако важно помнить, что если границы камеры были привязаны к удаленному слою, это может привести к неожиданному поведению. В данном примере границы заданы через второй слой, поэтому проблем не возникает.

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

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

Метод destroyLayer — мощный инструмент для динамического управления контентом тайловых карт в Phaser. Он позволяет освобождать ресурсы и изменять игровой мир в реальном времени. Для экспериментов попробуйте

  1. Уничтожать слой не по клику, а по таймеру или достижению игроком определенной области
  2. Проверить, как изменится производительность, если создавать и уничтожать слои циклически
  3. Добавить визуальные эффекты (частицы, анимацию) в момент уничтожения слоя для лучшей обратной связи
  4. Исследовать, что произойдет, если попытаться обновить уничтоженный слой — это поможет избежать ошибок в реальных проектах