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

В игровом мире всё вращается вокруг расстояний. Определяет ли враг игрока, достигает ли снаряд цели, или визуальный эффект реагирует на приближение — всё это требует расчёта дистанции. В этой статье мы разберём практический пример из официальной документации Phaser, который показывает, как просто и эффективно использовать `Phaser.Math.Distance.Between` для создания динамической визуализации радиуса действия между двумя спрайтами. Этот подход — основа для механик агро, индикаторов приближения и много другого.

Версия 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;
        }

        if (this.cursors.up.isDown)
        {
            this.player.y -= 5;
        }
        else if (this.cursors.down.isDown)
        {
            this.player.y += 5;
        }

        const dist = Phaser.Math.Distance.Between(this.player.x, this.player.y, this.ufo.x, this.ufo.y);

        this.graphic
            .clear()
            .strokeCircle(this.player.x, this.player.y, dist);
    }
}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

Подготовка сцены и загрузка ресурсов

Класс Example наследуется от Phaser.Scene. В методе preload() мы загружаем два изображения-спрайта с удалённого базового URL. Это стандартный подход для быстрого прототипирования с использованием ресурсов из репозитория примеров Phaser.

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.input.keyboard.createCursorKeys(), добавляются два спрайта на сцену в определённых координатах, а также создаётся объект Graphics для рисования. Обратите внимание, что this.graphic создаётся с предустановленным стилем линии голубого цвета.

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(). Здесь мы обрабатываем состояние клавиш-стрелок. В зависимости от нажатой клавиши, координаты `xилиyспрайтаthis.playerизменяются на фиксированную величину (5` пикселей). Это реализация самого простого пошагового управления.

if (this.cursors.left.isDown)
{
    this.player.x -= 5;
}
else if (this.cursors.right.isDown)
{
    this.player.x += 5;
}

if (this.cursors.up.isDown)
{
    this.player.y -= 5;
}
else if (this.cursors.down.isDown)
{
    this.player.y += 5;
}

Расчёт расстояния и динамическое рисование

Самая важная часть примера. На каждом кадре, после обновления позиции игрока, мы вычисляем расстояние между ним и НЛО с помощью статического метода Phaser.Math.Distance.Between. Метод принимает четыре аргумента: координаты X и Y первой точки (игрок) и координаты X и Y второй точки (НЛО), возвращая числовое значение — евклидово расстояние между ними.

const dist = Phaser.Math.Distance.Between(this.player.x, this.player.y, this.ufo.x, this.ufo.y);

Полученное расстояние dist используется как радиус для рисования круга. Объект this.graphic сначала очищается методом .clear() от рисунков предыдущего кадра, а затем методом .strokeCircle() рисуется контур круга. Центр круга привязан к координатам игрока, а его радиус равен текущему расстоянию до НЛО. Это создаёт эффект "радиуса действия" или "силового поля", которое постоянно меняет размер.

this.graphic
    .clear()
    .strokeCircle(this.player.x, this.player.y, dist);

Конфигурация и запуск игры

Это стандартный конфигурационный объект для Phaser 3. Он указывает, что рендеринг должен быть выбран автоматически (Phaser.AUTO), задаёт ID HTML-элемента для встраивания игры и определяет основную сцену.

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

Инициализация игры происходит созданием нового экземпляра Phaser.Game с переданной конфигурацией.

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

Пример наглядно демонстрирует, как всего одна строка кода с Phaser.Math.Distance.Between может стать мощным инструментом для геймдизайна и визуальной обратной связи. Для экспериментов попробуйте: изменить логику, чтобы круг рисовался вокруг НЛО; залить круг цветом, прозрачность которого зависит от расстояния; или добавить условие, при котором НЛО "просыпается" и начинает преследование, когда игрок заходит внутрь круга определённого радиуса.