О чем этот пример
Создание интерактивных игровых миров часто требует изменения окружения на лету. В статье разберем, как в Phaser реализовать замену тайлов на карте в ответ на действия игрока, используя метод `replaceByIndex`. Этот подход полезен для создания механик разрушения окружения, строительства или пошаговой стратегии, где игрок меняет ландшафт. Мы рассмотрим пример, где клик по любому тайлу на карте заменяет все его экземпляры на другое изображение. Вы научитесь получать координаты тайла из мировых координат мыши и применять массовые изменения к тайлсету, что станет основой для более сложных интерактивных систем.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
map;
controls;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('tiles', 'assets/tilemaps/tiles/tmw_desert_spacing.png');
this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/desert.json');
}
create ()
{
this.map = this.make.tilemap({ key: 'map' });
const tiles = this.map.addTilesetImage('Desert', 'tiles');
const layer = this.map.createLayer('Ground', tiles, 0, 0);
this.cameras.main.setBounds(0, 0, this.map.widthInPixels, this.map.heightInPixels);
const cursors = this.input.keyboard.createCursorKeys();
const controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
speed: 0.5
};
this.controls = new Phaser.Cameras.Controls.FixedKeyControl(controlConfig);
const help = this.add.text(16, 16, 'Click a tile to replace all instances with a plant.', {
fontSize: '18px',
padding: { x: 10, y: 5 },
backgroundColor: '#000000',
fill: '#ffffff'
});
help.setScrollFactor(0);
}
update (time, delta)
{
this.controls.update(delta);
if (this.input.manager.activePointer.isDown)
{
const worldPoint = this.input.activePointer.positionToCamera(this.cameras.main);
const tile = this.map.getTileAtWorldXY(worldPoint.x, worldPoint.y);
// This will replace all instances of the selected tile with a plant (tile id = 38).
this.map.replaceByIndex(tile.index, 38);
// You can also replace within a specific region (tileX, tileY, width, height):
// map.replaceByIndex(tile.index, 38, 5, 5, 15, 15);
}
}
}
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
pixelArt: true,
scene: Example
};
const game = new Phaser.Game(config);
Загрузка ресурсов и создание карты
В методе preload загружаются изображение тайлсета и JSON-файл карты, созданный в Tiled. Обратите внимание, что load.setBaseURL задает базовый URL для удобства, но в реальном проекте пути будут локальными.
В create создается тайловая карта (this.make.tilemap), к ней привязывается изображение тайлов (addTilesetImage) и создается слой (createLayer). Камера настраивается по границам карты, а управление через стрелки реализуется с помощью Phaser.Cameras.Controls.FixedKeyControl.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('tiles', 'assets/tilemaps/tiles/tmw_desert_spacing.png');
this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/desert.json');
}
create ()
{
this.map = this.make.tilemap({ key: 'map' });
const tiles = this.map.addTilesetImage('Desert', 'tiles');
const layer = this.map.createLayer('Ground', tiles, 0, 0);
this.cameras.main.setBounds(0, 0, this.map.widthInPixels, this.map.heightInPixels);
const controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
speed: 0.5
};
this.controls = new Phaser.Cameras.Controls.FixedKeyControl(controlConfig);
}
Обработка клика мыши и получение тайла
В методе update проверяется, нажата ли кнопка мыши (this.input.manager.activePointer.isDown). Если да, то позиция указателя преобразуется в мировые координаты камеры с помощью positionToCamera. Это важно, так как камера может быть смещена.
Затем метод this.map.getTileAtWorldXY возвращает объект тайла по мировым координатам. У этого объекта есть свойство index — уникальный идентификатор типа тайла в тайлсете.
if (this.input.manager.activePointer.isDown)
{
const worldPoint = this.input.activePointer.positionToCamera(this.cameras.main);
const tile = this.map.getTileAtWorldXY(worldPoint.x, worldPoint.y);
}
Массовая замена тайлов методом replaceByIndex
Ключевой метод — this.map.replaceByIndex. Он заменяет все тайлы с заданным индексом на новый индекс в пределах всей карты. В примере на место кликнутого тайла (tile.index) ставится тайл с индексом 38 (предположительно, изображение растения).
Метод также позволяет ограничить замену определенной областью, передав дополнительные параметры: начальные координаты тайла (tileX, tileY) и размер области (width, height). Это полезно для локальных изменений.
// Замена всех тайлов с индексом tile.index на тайл с индексом 38
this.map.replaceByIndex(tile.index, 38);
// Замена только в области 15x15 тайлов, начиная с позиции (5,5)
// this.map.replaceByIndex(tile.index, 38, 5, 5, 15, 15);
Настройка отображения и производительности
В коде используется pixelArt: true в конфигурации игры, что предотвращает размытие тайлов при масштабировании. Это важно для сохранения четкости пиксельной графики.
Текст с подсказкой создается в create и фиксируется на экране (setScrollFactor(0)), чтобы не двигаться с камерой. Управление камерой обновляется в update через this.controls.update(delta), обеспечивая плавное перемещение.
const help = this.add.text(16, 16, 'Click a tile to replace all instances with a plant.', {
fontSize: '18px',
padding: { x: 10, y: 5 },
backgroundColor: '#000000',
fill: '#ffffff'
});
help.setScrollFactor(0);
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
pixelArt: true,
scene: Example
};
Что попробовать дальше
Метод replaceByIndex — мощный инструмент для динамического изменения тайловых карт в Phaser. Он открывает возможности для механик типа «строительства» или «изменения ландшафта». Для экспериментов попробуйте: заменять тайлы только в определенной зоне вокруг игрока, использовать разные индексы для создания многослойных эффектов (например, вода -> земля -> трава) или комбинировать замену с физикой, добавляя новые коллайдеры. Это основа для создания живой, реагирующей на игрока среды.
