О чем этот пример
Работа с шестиугольными тайловыми картами в Phaser открывает новые возможности для создания стратегических игр. Однако преобразование координат из тайловых индексов в мировые координаты имеет свои нюансы. В этой статье мы разберем, как работает метод `tileToWorldXY` для шестиугольных слоев и как визуализировать этот процесс, чтобы лучше понимать пространственную логику вашей игры.
Версия 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.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 t = this.add.text(32, 550, 'x: 0 y: 0');
let x = 0;
let y = 0;
this.input.on('pointerdown', pointer => {
// const p = layer.tileToWorldXY(x, y);
// this.add.rectangle(p.x, p.y, 4, 4, 0xff0000);
const p1 = layer.tileToWorldXY(0, 0);
const p2 = layer.tileToWorldXY(1, 0);
const p3 = layer.tileToWorldXY(2, 0);
const p4 = layer.tileToWorldXY(0, 1);
const p5 = layer.tileToWorldXY(1, 1);
const p6 = layer.tileToWorldXY(2, 1);
this.add.rectangle(p1.x, p1.y, 4, 4, 0xff0000);
this.add.rectangle(p2.x, p2.y, 4, 4, 0xff0000);
this.add.rectangle(p3.x, p3.y, 4, 4, 0xff0000);
this.add.rectangle(p4.x, p4.y, 4, 4, 0xff0000);
this.add.rectangle(p5.x, p5.y, 4, 4, 0xff0000);
this.add.rectangle(p6.x, p6.y, 4, 4, 0xff0000);
});
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-LEFT', () => {
x--;
t.setText(`x: ${x} y: ${y}`);
});
this.input.keyboard.on('keydown-RIGHT', () => {
x++;
t.setText(`x: ${x} y: ${y}`);
});
this.input.keyboard.on('keydown-DOWN', () => {
y++;
t.setText(`x: ${x} y: ${y}`);
});
this.input.keyboard.on('keydown-UP', () => {
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);
Метод createLayer возвращает объект TilemapLayer, который содержит геометрическую информацию о сетке. Именно у этого объекта мы будем вызывать tileToWorldXY.
Как работает tileToWorldXY для шестиугольников
Метод tileToWorldXY(column, row) принимает индексы столбца и строки тайла в сетке карты (не в пикселях!) и возвращает объект Phaser.Math.Vector2 с мировыми координатами (x, y) центра этого тайла.
Для шестиугольной сетки расчет этих координат сложнее, чем для прямоугольной, из-за смещения строк и столбцов. Phaser учитывает тип сетки (например, pointy-top или flat-top), указанный в JSON-файле карты из Tiled, и автоматически применяет правильную формулу.
const worldPoint = layer.tileToWorldXY(tileX, tileY);
// worldPoint.x, worldPoint.y — координаты центра тайла в мире игры
Визуализация: расставляем маркеры по клику
В примере при клике мыши (pointerdown) вызывается код, который наглядно демонстрирует результат работы tileToWorldXY для первых шести тайлов.
const p1 = layer.tileToWorldXY(0, 0);
const p2 = layer.tileToWorldXY(1, 0);
const p3 = layer.tileToWorldXY(2, 0);
const p4 = layer.tileToWorldXY(0, 1);
const p5 = layer.tileToWorldXY(1, 1);
const p6 = layer.tileToWorldXY(2, 1);
Для каждой рассчитанной точки создается маленький красный прямоугольник, который ставится по полученным мировым координатам. Это позволяет увидеть, где именно Phaser располагает центр каждого шестиугольного тайла. Вы увидите, что центры тайлов в соседних строках смещены относительно друг друга.
Интерактивное исследование с клавиатуры
Исходный код содержит закомментированный блок, который позволяет исследовать координаты в интерактивном режиме с помощью клавиш-стрелок и пробела.
// Переменные x и y хранят текущий исследуемый тайл
let x = 0;
let y = 0;
// Обработчик пробела ставит маркер в текущую позицию (x, y)
this.input.keyboard.on('keydown-SPACE', () => {
const p = layer.tileToWorldXY(x, y);
this.add.rectangle(p.x, p.y, 4, 4, 0xff0000);
});
// Обработчики стрелок меняют x и y и обновляют текст на экране
// this.input.keyboard.on('keydown-LEFT', () => { x--; });
Раскомментировав этот блок, вы сможете перемещаться по сетке и ставить маркеры в любую позицию, наблюдая за паттерном расположения центров тайлов. Это отличный способ понять геометрию вашей конкретной шестиугольной карты.
Управление камерой для навигации
Поскольку карта может быть большой, в примере реализовано плавное управление камерой с помощью Phaser.Cameras.Controls.SmoothedKeyControl. Это позволяет свободно перемещаться и приближаться для детального изучения.
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);
Контроль камеры обновляется в методе update. Это стандартный подход в Phaser для обработки постоянного ввода, такого как удержание клавиш.
Что попробовать дальше
Метод tileToWorldXY — это мост между логической сеткой вашей карты и визуальным миром игры. Понимание того, как он работает для шестиугольников, критически важно для реализации механик выделения юнитов, расчета путей (pathfinding) или определения тайла под курсором. Для экспериментов попробуйте
- Раскомментировать блок с клавишами-стрелками и пошагово исследовать карту
- Изменить тип шестиугольной ориентации в Tiled и посмотреть, как изменится расчет координат
- Написать обратный метод — определение индекса тайла (worldToTileXY) по клику мыши, используя полученные знания о центрах
