О чем этот пример
Работа с гексагональными сетками — мощный инструмент для создания стратегий и ролевых игр. Однако их геометрия сложнее обычной прямоугольной. В примере демонстрируется, как Phaser позволяет получить точные координаты углов любого гексагонального тайла на карте. Это знание — ключ к реализации механик выделения, расчёта пути (pathfinding) или отрисовки пользовательских границ вокруг ячеек.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('tiles', 'assets/tilemaps/iso/tilesets/hex-tiles.png');
this.load.tilemapTiledJSON('map', 'assets/tilemaps/hex/10x10-test.json');
}
create ()
{
const map = this.add.tilemap('map');
const tileset = map.addTilesetImage('hex-tiles', 'tiles');
const layer = map.createLayer('Tile Layer 1', tileset);
layer.setAlpha(0.2);
// layer.setScale(2.8, 1.4);
const cursors = this.input.keyboard.createCursorKeys();
const controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
zoomIn: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q),
zoomOut: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E),
acceleration: 0.02,
drag: 0.0005,
maxSpeed: 0.7
};
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
const g = this.add.graphics();
g.lineStyle(2, 0x00ff00);
const t = this.add.text(32, 550, 'x: 0 y: 0');
let x = 0;
let y = 0;
this.input.on('pointerdown', pointer => {
const corners = layer.getTileCorners(x, y);
g.moveTo(corners[0].x, corners[0].y);
g.beginPath();
corners.forEach(corner => {
g.lineTo(corner.x, corner.y);
});
g.closePath();
g.strokePath();
});
this.input.keyboard.on('keydown-SPACE', () => {
const p = layer.tileToWorldXY(x, y);
this.add.rectangle(p.x, p.y, 4, 4, 0xff0000);
});
this.input.keyboard.on('keydown-A', () => {
x--;
t.setText(`x: ${x} y: ${y}`);
});
this.input.keyboard.on('keydown-D', () => {
x++;
t.setText(`x: ${x} y: ${y}`);
});
this.input.keyboard.on('keydown-S', () => {
y++;
t.setText(`x: ${x} y: ${y}`);
});
this.input.keyboard.on('keydown-W', () => {
y--;
t.setText(`x: ${x} y: ${y}`);
});
}
update (time, delta)
{
this.controls.update(delta);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
pixelArt: true,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка карты и камеры
Пример начинается с загрузки гексагональной тайловой карты, созданной в Tiled, и настройки интерактивной камеры.
const map = this.add.tilemap('map');
const tileset = map.addTilesetImage('hex-tiles', 'tiles');
const layer = map.createLayer('Tile Layer 1', tileset);
layer.setAlpha(0.2);
Прозрачность слоя (setAlpha(0.2)) установлена для того, чтобы позже нарисованные поверх зелёные контуры были хорошо видны. Управление камерой реализовано через SmoothedKeyControl, что позволяет плавно перемещаться и зумировать карту клавишами-стрелками и клавишами Q/E.
Ядро примера: метод getTileCorners
Основная логика сосредоточена в обработчике клика мыши. По клику для текущих координат тайла (x, y) вызывается ключевой метод.
const corners = layer.getTileCorners(x, y);
Метод getTileCorners() объекта тайлового слоя (layer) возвращает массив объектов. Каждый объект содержит координаты `xиy` одного из углов запрошенного гексагонального тайла. Для гексагона их будет шесть. Эти координаты уже пересчитаны в мировую систему координат (world coordinates) с учётом положения и масштаба слоя.
Визуализация результата
Полученный массив углов используется для отрисовки контура гексагона с помощью Graphics API.
g.moveTo(corners[0].x, corners[0].y);
g.beginPath();
corners.forEach(corner => {
g.lineTo(corner.x, corner.y);
});
g.closePath();
g.strokePath();
Код последовательно соединяет линиями все углы, начиная с первого, и замыкает путь, создавая готовый контур. Все контуры рисуются поверх полупрозрачного слоя карты, что позволяет наглядно убедиться в точности вычислений.
Навигация и вспомогательные методы
Для удобства исследования в примере реализовано управление индексами тайла (x, y) с клавиатуры (WASD) и два вспомогательных действия.
const p = layer.tileToWorldXY(x, y);
this.add.rectangle(p.x, p.y, 4, 4, 0xff0000);
По нажатию пробела (SPACE) в мировых координатах центра текущего тайла ставится красная точка. Метод tileToWorldXY — это ещё один полезный инструмент для перевода индексов тайла в точку на экране, часто используемый для позиционирования игровых объектов.
Что попробовать дальше
Методы getTileCorners() и tileToWorldXY() предоставляют разработчику точный контроль над геометрией гексагональной сетки. Это фундамент для множества игровых механик. Для экспериментов попробуйте: изменить масштаб слоя (раскомментировав layer.setScale), чтобы увидеть, как методы корректно работают с трансформациями; реализовать выделение тайла под курсором мыши; или использовать полученные углы для расчёта столкновений нестандартной формы.
