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

Работа с геометрией — мощный инструмент для создания визуальных эффектов в играх. В этом примере мы разберём, как использовать методы `Phaser.Geom.Line` для генерации динамического следа из вращающихся линий, который следует за курсором мыши. Этот приём может стать основой для визуализации магических заклинаний, энергетических следов или просто украшения интерфейса. Мы поэтапно разберём исходный код и объясним логику каждого действия.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        const graphics = this.add.graphics({ lineStyle: { width: 3, color: 0xaa00aa } });

        const pointerLine = new Phaser.Geom.Line(0, 0, 0, 200);

        const lines = [];

        for (let k = 0; k < 60; k++)
        {
            lines.push(new Phaser.Geom.Line(0, 0, 0, 0));
        }

        let i = 0;

        this.input.on('pointermove', pointer =>
        {

            Phaser.Geom.Line.CenterOn(pointerLine, pointer.x, pointer.y);

            Phaser.Geom.Line.CopyFrom(pointerLine, lines[i]);

            i = (i + 1) % lines.length;

            graphics.clear();

            graphics.strokeLineShape(pointerLine);

            for (let j = 0; j < lines.length; j++)
            {

                Phaser.Geom.Line.Rotate(lines[j], 0.2);

                graphics.strokeLineShape(lines[j]);

            }
        });
    }
}

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

const game = new Phaser.Game(config);

Инициализация сцены и создание графики

В методе create() сцены мы первым делом создаём объект Graphics, который будет отвечать за отрисовку всех линий. Мы задаём ему стиль линии: толщину 3 пикселя и пурпурный цвет.

Затем создаём основную линию pointerLine. Она будет центрироваться на позиции курсора и служить источником для копирования. Её начальные координаты — от (0, 0) до (0, 200), что задаёт вертикальную линию длиной 200 пикселей.

Далее мы создаём массив lines из 60 пустых линий. Они будут хранить историю положений и поворотов основной линии.

const graphics = this.add.graphics({ lineStyle: { width: 3, color: 0xaa00aa } });
const pointerLine = new Phaser.Geom.Line(0, 0, 0, 200);
const lines = [];
for (let k = 0; k < 60; k++)
{
    lines.push(new Phaser.Geom.Line(0, 0, 0, 0));
}
let i = 0;

Обработка движения курсора и копирование линии

Мы подписываемся на событие 'pointermove', которое срабатывает при каждом движении мыши. Внутри обработчика происходят ключевые действия.

Сначала метод Phaser.Geom.Line.CenterOn() перемещает основную линию pointerLine так, чтобы её центр совпал с текущими координатами курсора (pointer.x, pointer.y).

Затем метод Phaser.Geom.Line.CopyFrom() копирует текущее состояние pointerLine в одну из линий массива lines. Индекс `iуказывает, в какую именно линию копировать данные. После копирования индексi` увеличивается по модулю длины массива, обеспечивая циклическую перезапись старых линий.

this.input.on('pointermove', pointer =>
{
    Phaser.Geom.Line.CenterOn(pointerLine, pointer.x, pointer.y);
    Phaser.Geom.Line.CopyFrom(pointerLine, lines[i]);
    i = (i + 1) % lines.length;
    // ... дальнейший код отрисовки и вращения
});

Очистка, вращение и отрисовка линий

Перед каждой новой отрисовкой необходимо очистить холст Graphics от предыдущего кадра, иначе след будет накапливаться. Для этого используется метод graphics.clear().

После очистки мы рисуем основную линию pointerLine методом graphics.strokeLineShape().

Затем в цикле проходим по всем линиям в массиве lines. Для каждой линии вызываем Phaser.Geom.Line.Rotate(), который поворачивает её на 0.2 радиана вокруг её центра. Это создаёт эффект постепенного вращения старых линий. После поворота линия также отрисовывается.

Таким образом, мы видим основную линию под курсором и шлейф из 60 её предыдущих положений, которые непрерывно вращаются.

graphics.clear();
graphics.strokeLineShape(pointerLine);
for (let j = 0; j < lines.length; j++)
{
    Phaser.Geom.Line.Rotate(lines[j], 0.2);
    graphics.strokeLineShape(lines[j]);
}

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

Код завершается стандартной для Phaser 3 конфигурацией игры. Мы задаём размеры холста 800x600, указываем автоматический выбор рендерера (Phaser.AUTO), задаём ID родительского элемента в HTML и передаём класс нашей сцены Example.

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

const config = {
    width: 800,
    height: 600,
    type: Phaser.AUTO,
    parent: 'phaser-example',
    scene: Example
};
const game = new Phaser.Game(config);

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

Мы разобрали, как с помощью всего нескольких методов Phaser.Geom.LineCenterOn, CopyFrom и Rotate — создать живой визуальный эффект, реагирующий на ввод пользователя. Это отличная демонстрация работы с геометрическими объектами и графикой в реальном времени. **Идеи для экспериментов:** 1. Измените цвет, толщину или прозрачность (alpha) линий в зависимости от их возраста в массиве. 2. Замените вращение на масштабирование с помощью Phaser.Geom.Line.Scale. 3. Используйте этот паттерн не для курсора, а для движения игрового персонажа, создавая за ним энергетический след.