О чем этот пример
В процессе разработки игр часто возникает необходимость динамически управлять игровым миром, например, удалять целые области уровня для создания эффектов разрушения или оптимизации производительности. В 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. Он позволяет освобождать ресурсы и изменять игровой мир в реальном времени. Для экспериментов попробуйте
- Уничтожать слой не по клику, а по таймеру или достижению игроком определенной области
- Проверить, как изменится производительность, если создавать и уничтожать слои циклически
- Добавить визуальные эффекты (частицы, анимацию) в момент уничтожения слоя для лучшей обратной связи
- Исследовать, что произойдет, если попытаться обновить уничтоженный слой — это поможет избежать ошибок в реальных проектах
