О чем этот пример
Создание игровых уровней — фундаментальная задача в геймдеве. Phaser 3 предлагает мощную систему тайловых карт (Tilemaps), которая позволяет как загружать сложные уровни из редакторов (вроде Tiled), так и генерировать их прямо в коде. Этот подход идеально подходит для прототипирования, создания процедурных уровней или простых локаций, когда не хочется подключать внешние инструменты. В этой статье мы разберем, как создать и отобразить карту из обычного двумерного массива чисел, что даст вам полный контроль над уровнем прямо внутри вашего скрипта.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('mario-tiles', 'assets/tilemaps/tiles/super-mario.png');
}
create ()
{
// Load a map from a 2D array of tile indices
const level = [
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 1, 2, 3, 0, 0, 0, 1, 2, 3, 0 ],
[ 0, 5, 6, 7, 0, 0, 0, 5, 6, 7, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 14, 13, 14, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 14, 14, 14, 14, 14, 0, 0, 0, 15 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15 ],
[ 35, 36, 37, 0, 0, 0, 0, 0, 15, 15, 15 ],
[ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39 ]
];
// When loading from an array, make sure to specify the tileWidth and tileHeight
const map = this.make.tilemap({ data: level, tileWidth: 16, tileHeight: 16 });
const tiles = map.addTilesetImage('mario-tiles');
const layer = map.createLayer(0, tiles, 0, 0);
}
}
const config = {
type: Phaser.AUTO,
width: 11 * 16, // Number of tiles * size of the tile
height: 10 * 16,
zoom: 4,
parent: 'phaser-example',
pixelArt: true,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка: загрузка тайлсета
Перед созданием карты нам нужен набор изображений-тайлов — тайлсет. В методе preload() мы загружаем одно спрайт-изображение, которое содержит все необходимые тайлы, расположенные в виде сетки.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('mario-tiles', 'assets/tilemaps/tiles/super-mario.png');
}
Метод this.load.setBaseURL() задает базовый URL для последующих загрузок, что удобно, если все ресурсы хранятся в одной директории. Затем this.load.image() загружает изображение тайлсета и назначает ему ключ 'mario-tiles'. Этот ключ мы позже используем для создания тайлсета в памяти игры.
Данные уровня: карта как массив чисел
Сердце метода — представление уровня в виде двумерного массива (массива массивов). Каждое число в этом массиве соответствует индексу тайла в тайлсете. Ноль (`0`) обычно обозначает пустую клетку (отсутствие тайла).
const level = [
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 1, 2, 3, 0, 0, 0, 1, 2, 3, 0 ],
// ... остальные строки карты
[ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39 ]
];
Каждый вложенный массив — это строка (ось Y) на игровой карте. Каждый элемент внутри этого массива — столбец (ось X). Таким образом, level[1][0] (вторая строка, первый столбец) содержит значение `0`. Этот формат интуитивно понятен для чтения и редактирования прямо в коде, что отлично подходит для небольших уровней.
Создание карты и слоя
Создание игровой карты происходит в три этапа: создание объекта карты, создание тайлсета на основе загруженного изображения и, наконец, создание визуального слоя.
const map = this.make.tilemap({ data: level, tileWidth: 16, tileHeight: 16 });
const tiles = map.addTilesetImage('mario-tiles');
const layer = map.createLayer(0, tiles, 0, 0);
1. this.make.tilemap() создает объект тайловой карты. В его конфигурацию мы передаем наш массив level и критически важные параметры размера одного тайла: tileWidth и tileHeight. Без них Phaser не сможет корректно разрезать тайлсет и отобразить карту.
2. map.addTilesetImage() связывает загруженное изображение по ключу 'mario-tiles' с нашей картой, создавая тайлсет. Phaser автоматически разрежет изображение на тайлы, используя указанные ранее tileWidth и tileHeight.
3. map.createLayer() создает визуальный слой. Первый аргумент (`0) — это индекс слоя в карте (в нашем случае он один). Второй аргумент (tiles) — тайлсет для отрисовки. Третий и четвертый аргументы (0, 0`) — это координаты X и Y для отрисовки слоя на сцене.
Настройка конфигурации игры
Конфигурация игры (config) специально подогнана под наш пример, чтобы отобразить карту целиком и в увеличенном масштабе.
const config = {
type: Phaser.AUTO,
width: 11 * 16, // Number of tiles * size of the tile
height: 10 * 16,
zoom: 4,
parent: 'phaser-example',
pixelArt: true,
scene: Example
};
- width и height рассчитаны как количество тайлов в ширину и высоту карты, умноженное на размер тайла. Наш массив level имеет размер 11x11, но отображаемая область задана 11x10 (последняя строка с полом будет обрезана).
- zoom: 4 увеличивает весь холст игры в 4 раза, делая маленькие 16-пиксельные тайлы хорошо различимыми.
- pixelArt: true включает специальный режим рендеринга для пиксельной графики, который отключает сглаживание при масштабировании, сохраняя четкие границы пикселей.
- scene: Example указывает, что начальной сценой будет наш созданный класс.
Что попробовать дальше
Создание тайл-карт из массива — это мощный и быстрый способ наполнить ваш прототип игровым пространством. Вы получили полный контроль над структурой уровня прямо в JavaScript. Для экспериментов попробуйте
- изменить числа в массиве
level, чтобы спроектировать новую площадку - сгенерировать массив
levelпроцедурно, используя циклы и условия - добавить второй слой карты (например, для декораций поверх земли), создав еще один массив и вызвав
map.createLayer(1, tiles, 0, 0). Этот метод станет отличной основой для создания прототипов платформеров, головоломок или топ-даун уровней
