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

Работа с шестиугольными (гексагональными) картами открывает новые возможности для стратегических и пошаговых игр. Этот пример демонстрирует, как загрузить такую карту, создать на её основе слой и динамически добавлять новые тайлы во время выполнения игры. Вы научитесь манипулировать отдельными ячейками гексагональной сетки, что полезно для реализации механик строительства, изменения ландшафта или размещения юнитов.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    controls;

    preload ()
    {
        
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('tiles', 'assets/tilemaps/iso/tilesets/hexagonal-mini-6x.png');
        this.load.tilemapTiledJSON('map', 'assets/tilemaps/iso/hexagonal-mini-6x.json');
    }

    create ()
    {
        const map = this.make.tilemap({ key: 'map' });
        const tileset = map.addTilesetImage('hexmini-6x', 'tiles');
        const platforms = map.createLayer('Ground', tileset);

        const uiLayer = map.createBlankLayer('beep', tileset);
        uiLayer.putTileAt(2, 2, 2);
        uiLayer.putTileAt(2, 3, 2);

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

        this.cameras.main.setZoom(2);
        this.cameras.main.centerOn(200, 100);

        const controlConfig = {
            camera: this.cameras.main,
            left: cursors.left,
            right: cursors.right,
            up: cursors.up,
            down: cursors.down,
            acceleration: 0.02,
            drag: 0.0005,
            maxSpeed: 0.7
        };

        this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
    }

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

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

const game = new Phaser.Game(config);

Загрузка ресурсов: карта и тайлсет

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

В методе preload() мы загружаем PNG-изображение, содержащее все графические тайлы, и JSON-файл, который описывает структуру слоёв карты и их заполнение. Обратите внимание на базовый URL — он указывает на публичный репозиторий с примерами Phaser.

this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('tiles', 'assets/tilemaps/iso/tilesets/hexagonal-mini-6x.png');
this.load.tilemapTiledJSON('map', 'assets/tilemaps/iso/hexagonal-mini-6x.json');

Создание и отображение основного слоя карты

В методе create() происходит основная работа по инициализации карты. Сначала создаётся объект тайловой карты (Tilemap) по загруженному ключу. Затем из этой карты извлекается тайлсет по его имени внутри JSON-файла и связывается с загруженным изображением.

После этого создаётся слой Ground, который сразу отрисовывается на экране. Это статический фон нашей сцены.

const map = this.make.tilemap({ key: 'map' });
const tileset = map.addTilesetImage('hexmini-6x', 'tiles');
const platforms = map.createLayer('Ground', tileset);

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

Особенность этого примера — создание пустого динамического слоя поверх основного. Метод createBlankLayer() создаёт новый слой, связанный с тем же тайлсетом, но изначально не содержащий ни одного тайла.

На этом пустом слое uiLayer с помощью метода putTileAt() размещаются два тайла. Аргументы метода: индекс тайла в тайлсете (2), а также координаты X и Y в сетке карты. Это позволяет точечно изменять карту во время игры.

const uiLayer = map.createBlankLayer('beep', tileset);
uiLayer.putTileAt(2, 2, 2);
uiLayer.putTileAt(2, 3, 2);

Настройка камеры и управления

Чтобы лучше рассмотреть детали шестиугольной сетки, камера увеличивается (setZoom(2)) и смещается в определённую точку. Для удобства навигации по большой карте реализуется плавное управление камерой с клавиатуры с помощью SmoothedKeyControl. Этот контроллер обрабатывает нажатия стрелок, применяя ускорение и трение для создания инерции.

Конфигурация контроллера передаёт ссылки на клавиши и параметры физики движения камеры.

this.cameras.main.setZoom(2);
this.cameras.main.centerOn(200, 100);

const controlConfig = {
    camera: this.cameras.main,
    left: cursors.left,
    right: cursors.right,
    up: cursors.up,
    down: cursors.down,
    acceleration: 0.02,
    drag: 0.0005,
    maxSpeed: 0.7
};

this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);

Обновление состояния игры

В методе update() вызывается this.controls.update(delta). Это необходимо для того, чтобы обработчик управления камерой получал информацию о прошедшем времени (delta) и корректно рассчитывал её новую позицию с учётом инерции и нажатых клавиш. Без этого вызова камера не будет двигаться.

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

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

Пример наглядно показывает базовый пайплайн работы с шестиугольными тайл-картами в Phaser: от загрузки до программного редактирования. Для экспериментов попробуйте изменить индекс тайла в putTileAt, чтобы класть другие элементы, или вызывайте этот метод по клику мыши для интерактивного редактирования карты. Можно также создать несколько динамических слоёв для разделения игровых сущностей (например, юнитов, построек и эффектов).