О чем этот пример
При разработке игр часто требуется измерять расстояние между объектами. В Phaser для этого есть целый набор методов, и один из самых интересных — 'расстояние по Манхэттену', или 'змейка-расстояние' (`Snake`). В отличие от привычного прямого расстояния, оно измеряется как сумма разностей по горизонтали и вертикали, что идеально подходит для игр с сеточным или пошаговым движением, как в классических головоломках или тактических RPG. В этой статье мы разберем готовый пример, который не только вычисляет это расстояние между спрайтами, но и наглядно рисует вокруг игрока ромб, радиус которого равен этому самому 'змейка-расстоянию'. Этот прием поможет вам визуализировать зону досягаемости или область эффекта для персонажа.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
graphic;
ufo;
player;
cursors;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('eyes', 'assets/sprites/slimeeyes.png');
this.load.image('ufo', 'assets/sprites/ufo.png');
}
create ()
{
this.cursors = this.input.keyboard.createCursorKeys();
this.player = this.add.image(400, 300, 'eyes');
this.ufo = this.add.image(200, 150, 'ufo');
this.graphic = this.add.graphics({ lineStyle: { color: 0x00ffff } });
}
update ()
{
if (this.cursors.left.isDown)
{
this.player.x -= 5;
}
else if (this.cursors.right.isDown)
{
this.player.x += 5;
}
else if (this.cursors.up.isDown)
{
this.player.y -= 5;
}
else if (this.cursors.down.isDown)
{
this.player.y += 5;
}
const dist = Phaser.Math.Distance.Snake(this.player.x, this.player.y, this.ufo.x, this.ufo.y);
this.graphic
.clear()
.strokePoints([
{ x: this.player.x + dist, y: this.player.y },
{ x: this.player.x, y: this.player.y + dist },
{ x: this.player.x - dist, y: this.player.y },
{ x: this.player.x , y: this.player.y - dist }
], true, true);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ресурсов
В методе preload мы загружаем два изображения с удаленного сервера. Одно будет представлять игрока, другое — статичный объект (НЛО), до которого будем считать расстояние.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('eyes', 'assets/sprites/slimeeyes.png');
this.load.image('ufo', 'assets/sprites/ufo.png');
}
В методе create инициализируются ключевые объекты сцены. Создается управление с клавиатуры, оба спрайта размещаются на поле, а также создается объект Graphics для последующего рисования.
create ()
{
this.cursors = this.input.keyboard.createCursorKeys();
this.player = this.add.image(400, 300, 'eyes');
this.ufo = this.add.image(200, 150, 'ufo');
this.graphic = this.add.graphics({ lineStyle: { color: 0x00ffff } });
}
Управление игроком и вычисление расстояния
Логика движения игрока обрабатывается в update. При нажатии клавиш стрелок координаты спрайта player меняются с фиксированным шагом.
if (this.cursors.left.isDown)
{
this.player.x -= 5;
}
else if (this.cursors.right.isDown)
{
this.player.x += 5;
}
else if (this.cursors.up.isDown)
{
this.player.y -= 5;
}
else if (this.cursors.down.isDown)
{
this.player.y += 5;
}
Сразу после этого вычисляется ключевая метрика — 'змейка-расстояние' между игроком и НЛО с помощью Phaser.Math.Distance.Snake. Эта функция возвращает сумму абсолютных разностей по осям X и Y.
const dist = Phaser.Math.Distance.Snake(this.player.x, this.player.y, this.ufo.x, this.ufo.y);
Визуализация расстояния в виде ромба
Полученное расстояние dist используется как радиус для рисования фигуры. Код каждый кадр очищает старую графику и рисует замкнутую ломаную линию, соединяя четыре точки.
this.graphic
.clear()
.strokePoints([
{ x: this.player.x + dist, y: this.player.y },
{ x: this.player.x, y: this.player.y + dist },
{ x: this.player.x - dist, y: this.player.y },
{ x: this.player.x , y: this.player.y - dist }
], true, true);
Эти точки расположены справа, снизу, слева и сверху от центра игрока. Параметр true замыкает контур, создавая ромб. Важно понимать: в данном контексте dist — это не гипотенуза (евклидово расстояние), а сумма катетов. Поэтому все вершины ромба отстоят от центра ровно на это значение, если двигаться строго по осям, что и соответствует логике 'расстояния по Манхэттену'.
Конфигурация и запуск игры
Стандартная конфигурация движка Phaser 3. Указывается автоматический выбор рендерера, родительский HTML-элемент и класс нашей сцены.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Метод Phaser.Math.Distance.Snake — это мощный и простой инструмент для игр, где движение ограничено сеткой или осями. Визуализация в виде ромба делает это расстояние осязаемым на экране.
Для экспериментов попробуйте:
1. Заменить Snake на Distance.Between и нарисовать круг, чтобы увидеть разницу между метриками.
2. Сделать так, чтобы НЛО 'убегало' от игрока, если 'змейка-расстояние' становится меньше определенного порога.
3. Использовать это расстояние для расчета урона, который уменьшается по мере отдаления цели.
