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

Phaser предоставляет мощный инструмент для работы с тайловыми картами через модуль Tilemap. Тайловая карта состоит из слоев, в которых каждый тайл имеет определённый индекс, соответствующий текстуре из тайлсета. Основная задача при динамическом создании карты из массива — преобразовать двумерный массив чисел в слой тайлов, корректно сопоставив индексы тайлов с графикой.

const mapData = [
    [0, 1, 1, 0, 2],
    [1, 1, 0, 2, 2],
    [0, 2, 2, 1, 0]
];

В этом примере числа 0, 1, 2 представляют разные типы тайлов: пустой, земля, вода и т.д.


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

Для начала необходимо создать объект карты без привязки к файлу JSON:

const map = this.make.tilemap({ tileWidth: 32, tileHeight: 32, width: mapData[0].length, height: mapData.length });
  • tileWidth и tileHeight — размеры одного тайла в пикселях.
  • width и height — размеры карты в тайлах.

После этого создаётся тайлсет:

const tileset = map.addTilesetImage('terrain', null, 32, 32);
  • 'terrain' — ключ загруженной текстуры тайлсета.
  • null указывает, что используется один тайлсет без ассоциированного JSON.
  • 32x32 — размеры тайла в текстуре.

Создание слоя и заполнение тайлами из массива

Для отображения карты нужно создать слой:

const layer = map.createBlankLayer('Layer1', tileset);

Далее осуществляется заполнение слоя на основе массива:

for (let y = 0; y < mapData.length; y++) {
    for (let x = 0; x < mapData[y].length; x++) {
        const tileIndex = mapData[y][x];
        if (tileIndex >= 0) {
            layer.putTileAt(tileIndex, x, y);
        }
    }
}
  • putTileAt(index, x, y) помещает тайл с заданным индексом на координаты (x, y) слоя.
  • Проверка tileIndex >= 0 позволяет пропускать пустые тайлы, если 0 соответствует прозрачной области.

Динамическое обновление карты

Phaser позволяет изменять тайлы в реальном времени, что важно для создания интерактивных уровней:

layer.putTileAt(2, 1, 0); // заменяет тайл в позиции (1,0) на воду
layer.removeTileAt(3, 2); // удаляет тайл в позиции (3,2)

Использование layer.setTileIndexCallback(index, callback, context) позволяет реагировать на взаимодействие с конкретными тайлами:

layer.setTileIndexCallback(2, (sprite, tile) => {
    sprite.setVelocity(0); // остановка игрока на воде
}, this);

Генерация карты случайным образом

Для создания процедурно сгенерированных уровней удобно использовать генерацию массива перед созданием тайлов:

const width = 20;
const height = 15;
const proceduralMap = Array.from({ length: height }, () =>
    Array.from({ length: width }, () => Math.floor(Math.random() * 3))
);

Здесь каждый элемент массива случайно принимает значения 0, 1 или 2, что позволяет создавать уникальные карты на лету. После этого массив используется в том же цикле putTileAt.


Оптимизация производительности

При работе с большими картами важно минимизировать количество операций putTileAt во время обновления. Рекомендуется:

  • Создавать массивы тайлов заранее.
  • Использовать fill для однотипных областей.
  • Обновлять только изменённые тайлы, а не весь слой.

Пример заполнения области одним индексом:

layer.fill(1, 0, 0, width, height); // заполняет прямоугольник землёй

Работа с коллизиями

Для интерактивных игр важна коллизия тайлов. Phaser поддерживает её через слой:

layer.setCollision([1, 2]); // тайлы с индексами 1 и 2 вызывают коллизию
this.physics.add.collider(player, layer);
  • Массив [1, 2] — индексы тайлов, с которыми игрок может сталкиваться.
  • Метод this.physics.add.collider связывает слой с объектами физики.

Итоговый подход

Динамическое создание тайловой карты сводится к следующей последовательности:

  1. Создание двумерного массива, задающего карту.
  2. Инициализация пустого Tilemap с размерами и тайлсетом.
  3. Создание слоя через createBlankLayer.
  4. Заполнение слоя с помощью putTileAt или fill.
  5. Настройка коллизий и обработчиков взаимодействий.
  6. Возможное динамическое обновление тайлов в процессе игры.

Такой подход обеспечивает гибкость: карты могут быть статическими, загруженными из JSON, или полностью процедурными, с динамической модификацией во время игры.